@microsoft/rayfin-client 1.35.0-alpha.1412 → 1.35.0-beta.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/client.d.ts +33 -1
- package/dist/client.js +28 -0
- package/dist/config.d.ts +128 -0
- package/dist/config.js +189 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +7 -6
package/dist/client.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { type EntitySchema, createDataApi } from '@microsoft/rayfin-data';
|
|
|
8
8
|
import { createFunctionsApi, type FunctionsSchema } from '@microsoft/rayfin-functions';
|
|
9
9
|
import { SdkError, NetworkError } from '@microsoft/rayfin-lib';
|
|
10
10
|
import { ApiClient, ApiClientConfig } from '@microsoft/rayfin-lib';
|
|
11
|
+
import { type RayfinRuntimeConfig } from './config.js';
|
|
11
12
|
/**
|
|
12
13
|
* Auth error specific to the Rayfin SDK.
|
|
13
14
|
*/
|
|
@@ -36,6 +37,8 @@ export interface RayfinClientConfig extends ApiClientConfig {
|
|
|
36
37
|
autoRefreshToken?: boolean;
|
|
37
38
|
/** When false, StorageEvent listener is not registered. Default: auto-detected (true in browser with localStorage). */
|
|
38
39
|
multiTabSync?: boolean;
|
|
40
|
+
/** Values passed through to `resolveRayfinConfig()`'s `runtimeConfig` result when constructed via that helper (e.g. build-time `VITE_*` vars as defaults); exposed as `client.runtimeConfig`. */
|
|
41
|
+
runtimeConfig?: RayfinRuntimeConfig;
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Configuration for the server/worker-oriented {@link RayfinServerClient}.
|
|
@@ -45,13 +48,17 @@ export interface RayfinClientConfig extends ApiClientConfig {
|
|
|
45
48
|
export interface RayfinServerClientConfig extends Omit<ApiClientConfig, 'getAccessToken'> {
|
|
46
49
|
/** Access token (string or function) for Authorization: Bearer <token>. Use the function form to rotate per request. */
|
|
47
50
|
accessToken?: string | (() => string | null);
|
|
51
|
+
/** Values passed through to `resolveRayfinConfig()`'s `runtimeConfig` result when constructed via that helper (e.g. build-time env vars as defaults); exposed as `client.runtimeConfig`. */
|
|
52
|
+
runtimeConfig?: RayfinRuntimeConfig;
|
|
48
53
|
}
|
|
49
54
|
/** Shared base for Rayfin clients: owns the typed data API and the underlying HTTP client. */
|
|
50
55
|
export declare abstract class RayfinClientBase<TSchema extends EntitySchema = Record<string, any>> {
|
|
51
56
|
/** Typed data API for querying and mutating your entities. */
|
|
52
57
|
readonly data: ReturnType<typeof createDataApi<TSchema>>;
|
|
53
58
|
protected apiClient: ApiClient;
|
|
54
|
-
protected constructor(config: ApiClientConfig
|
|
59
|
+
protected constructor(config: ApiClientConfig & {
|
|
60
|
+
runtimeConfig?: RayfinRuntimeConfig;
|
|
61
|
+
});
|
|
55
62
|
/** SDK error classes, exposed for convenient `instanceof` checks. */
|
|
56
63
|
static readonly errors: {
|
|
57
64
|
SdkError: typeof SdkError;
|
|
@@ -84,12 +91,22 @@ export declare abstract class RayfinClientBase<TSchema extends EntitySchema = Re
|
|
|
84
91
|
* .where({ isActive: true })
|
|
85
92
|
* .execute();
|
|
86
93
|
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example ALM-aware deployments — resolve runtime config, then construct
|
|
96
|
+
* ```typescript
|
|
97
|
+
* import { RayfinClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
98
|
+
*
|
|
99
|
+
* const resolved = await resolveRayfinConfig({ baseUrl, publishableKey });
|
|
100
|
+
* const client = new RayfinClient({ ...resolved, authStorage: true });
|
|
101
|
+
* ```
|
|
87
102
|
*/
|
|
88
103
|
export declare class RayfinClient<TSchema extends EntitySchema = Record<string, any>, TFunctionsSchema extends FunctionsSchema = FunctionsSchema> extends RayfinClientBase<TSchema> {
|
|
89
104
|
/** Authentication operations (sign up, sign in, sign out, session state). */
|
|
90
105
|
readonly auth: Auth;
|
|
91
106
|
/** Typed, schema-aware accessors for invoking your backend functions. */
|
|
92
107
|
readonly functions: ReturnType<typeof createFunctionsApi<TFunctionsSchema>>;
|
|
108
|
+
/** All resolved `rayfin.config.json` values, for apps that need more than just `baseUrl`/`publishableKey` (e.g. Fabric embed auth coordinates). */
|
|
109
|
+
readonly runtimeConfig?: RayfinRuntimeConfig;
|
|
93
110
|
/**
|
|
94
111
|
* Creates a browser Rayfin client and wires up authentication.
|
|
95
112
|
*
|
|
@@ -111,6 +128,19 @@ export declare class RayfinClient<TSchema extends EntitySchema = Record<string,
|
|
|
111
128
|
*
|
|
112
129
|
* const todos = await client.data.Todo.select(['id', 'title']).execute();
|
|
113
130
|
* ```
|
|
131
|
+
*
|
|
132
|
+
* @example ALM-aware deployments — resolve runtime config, then construct
|
|
133
|
+
* ```typescript
|
|
134
|
+
* import { RayfinServerClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
135
|
+
*
|
|
136
|
+
* // Node.js/workers have no implicit document origin, so pass an absolute
|
|
137
|
+
* // configUrl to enable remote config loading.
|
|
138
|
+
* const resolved = await resolveRayfinConfig(
|
|
139
|
+
* { baseUrl, publishableKey },
|
|
140
|
+
* { configUrl: `${process.env.RAYFIN_API_URL}/rayfin.config.json` }
|
|
141
|
+
* );
|
|
142
|
+
* const client = new RayfinServerClient({ ...resolved, accessToken });
|
|
143
|
+
* ```
|
|
114
144
|
*/
|
|
115
145
|
export declare class RayfinServerClient<TSchema extends EntitySchema = Record<string, any>> extends RayfinClientBase<TSchema> {
|
|
116
146
|
/**
|
|
@@ -119,6 +149,8 @@ export declare class RayfinServerClient<TSchema extends EntitySchema = Record<st
|
|
|
119
149
|
* @param config - Client configuration, including the access token (string or
|
|
120
150
|
* function form for per-request rotation).
|
|
121
151
|
*/
|
|
152
|
+
/** All resolved `rayfin.config.json` values, for callers that need more than just `baseUrl`/`publishableKey`. */
|
|
153
|
+
readonly runtimeConfig?: RayfinRuntimeConfig;
|
|
122
154
|
constructor(config: RayfinServerClientConfig);
|
|
123
155
|
}
|
|
124
156
|
export default RayfinClient;
|
package/dist/client.js
CHANGED
|
@@ -49,6 +49,7 @@ export class RayfinClientBase {
|
|
|
49
49
|
functionsBaseUrl: config.functionsBaseUrl,
|
|
50
50
|
useProxy: config.useProxy,
|
|
51
51
|
headers: config.headers,
|
|
52
|
+
moniker: config.runtimeConfig?.itemId,
|
|
52
53
|
timeout: config.timeout,
|
|
53
54
|
getAccessToken: config.getAccessToken,
|
|
54
55
|
});
|
|
@@ -87,12 +88,22 @@ export class RayfinClientBase {
|
|
|
87
88
|
* .where({ isActive: true })
|
|
88
89
|
* .execute();
|
|
89
90
|
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example ALM-aware deployments — resolve runtime config, then construct
|
|
93
|
+
* ```typescript
|
|
94
|
+
* import { RayfinClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
95
|
+
*
|
|
96
|
+
* const resolved = await resolveRayfinConfig({ baseUrl, publishableKey });
|
|
97
|
+
* const client = new RayfinClient({ ...resolved, authStorage: true });
|
|
98
|
+
* ```
|
|
90
99
|
*/
|
|
91
100
|
export class RayfinClient extends RayfinClientBase {
|
|
92
101
|
/** Authentication operations (sign up, sign in, sign out, session state). */
|
|
93
102
|
auth;
|
|
94
103
|
/** Typed, schema-aware accessors for invoking your backend functions. */
|
|
95
104
|
functions;
|
|
105
|
+
/** All resolved `rayfin.config.json` values, for apps that need more than just `baseUrl`/`publishableKey` (e.g. Fabric embed auth coordinates). */
|
|
106
|
+
runtimeConfig;
|
|
96
107
|
/**
|
|
97
108
|
* Creates a browser Rayfin client and wires up authentication.
|
|
98
109
|
*
|
|
@@ -108,6 +119,7 @@ export class RayfinClient extends RayfinClientBase {
|
|
|
108
119
|
});
|
|
109
120
|
this.auth.attachToClient(this.apiClient);
|
|
110
121
|
this.functions = createFunctionsApi(this.apiClient);
|
|
122
|
+
this.runtimeConfig = config.runtimeConfig;
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
/**
|
|
@@ -124,6 +136,19 @@ export class RayfinClient extends RayfinClientBase {
|
|
|
124
136
|
*
|
|
125
137
|
* const todos = await client.data.Todo.select(['id', 'title']).execute();
|
|
126
138
|
* ```
|
|
139
|
+
*
|
|
140
|
+
* @example ALM-aware deployments — resolve runtime config, then construct
|
|
141
|
+
* ```typescript
|
|
142
|
+
* import { RayfinServerClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
143
|
+
*
|
|
144
|
+
* // Node.js/workers have no implicit document origin, so pass an absolute
|
|
145
|
+
* // configUrl to enable remote config loading.
|
|
146
|
+
* const resolved = await resolveRayfinConfig(
|
|
147
|
+
* { baseUrl, publishableKey },
|
|
148
|
+
* { configUrl: `${process.env.RAYFIN_API_URL}/rayfin.config.json` }
|
|
149
|
+
* );
|
|
150
|
+
* const client = new RayfinServerClient({ ...resolved, accessToken });
|
|
151
|
+
* ```
|
|
127
152
|
*/
|
|
128
153
|
export class RayfinServerClient extends RayfinClientBase {
|
|
129
154
|
/**
|
|
@@ -132,6 +157,8 @@ export class RayfinServerClient extends RayfinClientBase {
|
|
|
132
157
|
* @param config - Client configuration, including the access token (string or
|
|
133
158
|
* function form for per-request rotation).
|
|
134
159
|
*/
|
|
160
|
+
/** All resolved `rayfin.config.json` values, for callers that need more than just `baseUrl`/`publishableKey`. */
|
|
161
|
+
runtimeConfig;
|
|
135
162
|
constructor(config) {
|
|
136
163
|
super(config);
|
|
137
164
|
if (config.accessToken !== undefined) {
|
|
@@ -140,6 +167,7 @@ export class RayfinServerClient extends RayfinClientBase {
|
|
|
140
167
|
: () => config.accessToken;
|
|
141
168
|
this.apiClient.setAccessTokenCallback(tokenFn);
|
|
142
169
|
}
|
|
170
|
+
this.runtimeConfig = config.runtimeConfig;
|
|
143
171
|
}
|
|
144
172
|
}
|
|
145
173
|
// Default export remains the browser client for backward compatibility.
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration loading for Rayfin SPAs.
|
|
3
|
+
*
|
|
4
|
+
* Instead of baking environment-specific values into the bundle at build time
|
|
5
|
+
* (via `import.meta.env.VITE_*`), the SPA fetches a `rayfin.config.json` file
|
|
6
|
+
* at startup. This allows the same compiled bundle to work across all
|
|
7
|
+
* environments — only the config file changes per stage.
|
|
8
|
+
*
|
|
9
|
+
* The file is a deploy-time artifact: `rayfin up` emits it into the static
|
|
10
|
+
* build so it is bundled and served alongside the app, then removes it from the
|
|
11
|
+
* working tree afterward. In local development the file is intentionally absent
|
|
12
|
+
* — `rayfin dev` relies on build-time `VITE_*` values instead — so
|
|
13
|
+
* `loadRayfinConfig` returns `null` and the SPA falls back to those values.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when runtime configuration loading fails.
|
|
17
|
+
*/
|
|
18
|
+
export declare class RayfinConfigError extends Error {
|
|
19
|
+
readonly code: string;
|
|
20
|
+
constructor(message: string, code: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Runtime configuration shape for a Rayfin SPA.
|
|
24
|
+
* Loaded from `rayfin.config.json` at startup — contains only
|
|
25
|
+
* deployment-specific values that change per environment.
|
|
26
|
+
* Service mode (mock / rayfin / fabric) is controlled separately
|
|
27
|
+
* via `VITE_SERVICE_MODE` and is not part of this config.
|
|
28
|
+
*/
|
|
29
|
+
export interface RayfinConfig {
|
|
30
|
+
/** Base URL of the Rayfin backend API. */
|
|
31
|
+
apiUrl: string;
|
|
32
|
+
/** Publishable key for service-level authentication. */
|
|
33
|
+
publishableKey?: string;
|
|
34
|
+
/** Fabric workspace ID (present in Fabric deployments). */
|
|
35
|
+
workspaceId?: string;
|
|
36
|
+
/** Fabric item ID (present in Fabric deployments). */
|
|
37
|
+
itemId?: string;
|
|
38
|
+
/** Fabric portal URL (present in Fabric deployments). */
|
|
39
|
+
portalUrl?: string;
|
|
40
|
+
/** Fabric tenant ID (present in Fabric deployments). */
|
|
41
|
+
tenantId?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Loads the Rayfin runtime configuration from the well-known config file.
|
|
45
|
+
*
|
|
46
|
+
* @param path - Override the config file URL (defaults to `/rayfin.config.json`).
|
|
47
|
+
* @returns The validated runtime configuration, or `null` if the config is
|
|
48
|
+
* genuinely absent (HTTP 404, an HTML SPA fallback body, or — in a
|
|
49
|
+
* non-browser runtime with no explicit `configUrl` — a relative path with
|
|
50
|
+
* no origin to resolve against). Throws {@link RayfinConfigError} if the
|
|
51
|
+
* file could not be loaded for any other reason (`CONFIG_LOAD_FAILED`,
|
|
52
|
+
* e.g. a 5xx or 401/403 response, or a fetch-level failure such as a
|
|
53
|
+
* network error — these are real failures, not absence, and are not
|
|
54
|
+
* silently swallowed), or if the file is present but malformed or
|
|
55
|
+
* incomplete (`CONFIG_PARSE_FAILED`, `CONFIG_INVALID`, `CONFIG_INCOMPLETE`).
|
|
56
|
+
*
|
|
57
|
+
* Not part of the public API — {@link resolveRayfinConfig} calls this
|
|
58
|
+
* internally. Prefer that over calling this directly.
|
|
59
|
+
*/
|
|
60
|
+
export declare function loadRayfinConfig(path?: string): Promise<RayfinConfig | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Options controlling how {@link resolveRayfinConfig} fetches remote runtime
|
|
63
|
+
* configuration. To skip remote loading entirely, don't call it — construct
|
|
64
|
+
* the client directly from your own values instead.
|
|
65
|
+
*/
|
|
66
|
+
export interface ResolveRayfinConfigOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Absolute URL to fetch the runtime config from, overriding the default
|
|
69
|
+
* relative `/rayfin.config.json`.
|
|
70
|
+
*
|
|
71
|
+
* The default relative path only resolves in a browser, where `fetch`
|
|
72
|
+
* implicitly uses the page's document origin. Non-browser runtimes (e.g.
|
|
73
|
+
* `RayfinServerClient` in Node.js) have no such origin, so omitting
|
|
74
|
+
* `configUrl` there skips the fetch entirely and falls back to
|
|
75
|
+
* caller-supplied values, rather than attempting a relative fetch that
|
|
76
|
+
* would always reject. Pass an absolute `configUrl` (e.g.
|
|
77
|
+
* `${process.env.RAYFIN_API_URL}/rayfin.config.json`) to enable remote
|
|
78
|
+
* config loading outside the browser — once set (or in the browser, where
|
|
79
|
+
* a fetch is always attempted), a fetch failure (unreachable host,
|
|
80
|
+
* DNS/TLS error, offline, etc.) throws {@link RayfinConfigError}
|
|
81
|
+
* (`CONFIG_LOAD_FAILED`) rather than silently falling back, since it's a
|
|
82
|
+
* real failure, not evidence of absence.
|
|
83
|
+
*/
|
|
84
|
+
configUrl?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* All `rayfin.config.json` values as a single optional-field bag — every
|
|
88
|
+
* field is optional because neither the remote config nor a caller-supplied
|
|
89
|
+
* set of defaults is guaranteed to provide it. Used both as the defaults
|
|
90
|
+
* shape (typically build-time `VITE_*` values) and as the resolved shape
|
|
91
|
+
* returned by {@link resolveRayfinConfig}.
|
|
92
|
+
*/
|
|
93
|
+
export interface RayfinRuntimeConfig {
|
|
94
|
+
/** Base URL of the Rayfin backend API. */
|
|
95
|
+
apiUrl?: string;
|
|
96
|
+
/** Publishable key for service-level authentication. */
|
|
97
|
+
publishableKey?: string;
|
|
98
|
+
/** Fabric workspace ID (present in Fabric deployments). */
|
|
99
|
+
workspaceId?: string;
|
|
100
|
+
/** Fabric item ID (present in Fabric deployments). */
|
|
101
|
+
itemId?: string;
|
|
102
|
+
/** Fabric portal URL (present in Fabric deployments). */
|
|
103
|
+
portalUrl?: string;
|
|
104
|
+
/** Fabric tenant ID (present in Fabric deployments). */
|
|
105
|
+
tenantId?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolves the config a `RayfinClient` / `RayfinServerClient` should be
|
|
109
|
+
* constructed with: loads the remote `rayfin.config.json` (unless the caller
|
|
110
|
+
* skips calling this function entirely), overlays it over the supplied
|
|
111
|
+
* `defaults` per-field, and returns the merged `baseUrl` / `publishableKey` /
|
|
112
|
+
* `runtimeConfig`. Construction itself is a plain, synchronous
|
|
113
|
+
* `new RayfinClient(...)` call — this function only resolves values.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* import { RayfinClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
118
|
+
*
|
|
119
|
+
* const resolved = await resolveRayfinConfig({ baseUrl, publishableKey });
|
|
120
|
+
* const client = new RayfinClient({ ...resolved, authStorage: true });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export declare function resolveRayfinConfig(defaults: Pick<RayfinRuntimeConfig, 'apiUrl' | 'publishableKey'> & Partial<RayfinRuntimeConfig>, options?: ResolveRayfinConfigOptions): Promise<{
|
|
124
|
+
baseUrl?: string;
|
|
125
|
+
publishableKey?: string;
|
|
126
|
+
runtimeConfig: RayfinRuntimeConfig;
|
|
127
|
+
}>;
|
|
128
|
+
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration loading for Rayfin SPAs.
|
|
3
|
+
*
|
|
4
|
+
* Instead of baking environment-specific values into the bundle at build time
|
|
5
|
+
* (via `import.meta.env.VITE_*`), the SPA fetches a `rayfin.config.json` file
|
|
6
|
+
* at startup. This allows the same compiled bundle to work across all
|
|
7
|
+
* environments — only the config file changes per stage.
|
|
8
|
+
*
|
|
9
|
+
* The file is a deploy-time artifact: `rayfin up` emits it into the static
|
|
10
|
+
* build so it is bundled and served alongside the app, then removes it from the
|
|
11
|
+
* working tree afterward. In local development the file is intentionally absent
|
|
12
|
+
* — `rayfin dev` relies on build-time `VITE_*` values instead — so
|
|
13
|
+
* `loadRayfinConfig` returns `null` and the SPA falls back to those values.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when runtime configuration loading fails.
|
|
17
|
+
*/
|
|
18
|
+
export class RayfinConfigError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
constructor(message, code) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'RayfinConfigError';
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Default path for the runtime config file (served from `public/`). */
|
|
27
|
+
const DEFAULT_CONFIG_PATH = '/rayfin.config.json';
|
|
28
|
+
/**
|
|
29
|
+
* Loads the Rayfin runtime configuration from the well-known config file.
|
|
30
|
+
*
|
|
31
|
+
* @param path - Override the config file URL (defaults to `/rayfin.config.json`).
|
|
32
|
+
* @returns The validated runtime configuration, or `null` if the config is
|
|
33
|
+
* genuinely absent (HTTP 404, an HTML SPA fallback body, or — in a
|
|
34
|
+
* non-browser runtime with no explicit `configUrl` — a relative path with
|
|
35
|
+
* no origin to resolve against). Throws {@link RayfinConfigError} if the
|
|
36
|
+
* file could not be loaded for any other reason (`CONFIG_LOAD_FAILED`,
|
|
37
|
+
* e.g. a 5xx or 401/403 response, or a fetch-level failure such as a
|
|
38
|
+
* network error — these are real failures, not absence, and are not
|
|
39
|
+
* silently swallowed), or if the file is present but malformed or
|
|
40
|
+
* incomplete (`CONFIG_PARSE_FAILED`, `CONFIG_INVALID`, `CONFIG_INCOMPLETE`).
|
|
41
|
+
*
|
|
42
|
+
* Not part of the public API — {@link resolveRayfinConfig} calls this
|
|
43
|
+
* internally. Prefer that over calling this directly.
|
|
44
|
+
*/
|
|
45
|
+
export async function loadRayfinConfig(path = DEFAULT_CONFIG_PATH) {
|
|
46
|
+
if (typeof window === 'undefined' && path === DEFAULT_CONFIG_PATH) {
|
|
47
|
+
// Non-browser runtime with no explicit configUrl: a relative fetch has
|
|
48
|
+
// no origin to resolve against and would always reject. Skip it rather
|
|
49
|
+
// than triggering (and swallowing) that rejection — callers that want
|
|
50
|
+
// remote config outside the browser must pass an absolute configUrl.
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
let response;
|
|
54
|
+
try {
|
|
55
|
+
response = await fetch(path);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
// A fetch-level failure (offline, DNS, CORS, a transient network error)
|
|
59
|
+
// is a real failure, not absence — local-dev absence is identified below
|
|
60
|
+
// via 404 / the SPA HTML fallback, once a response is actually received.
|
|
61
|
+
// Swallowing this would let a promoted Prod bundle silently keep using
|
|
62
|
+
// its compiled Dev endpoint and key.
|
|
63
|
+
throw new RayfinConfigError(`Failed to load Rayfin config from "${path}": ` +
|
|
64
|
+
`${err instanceof Error ? err.message : String(err)}.`, 'CONFIG_LOAD_FAILED');
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
if (response.status === 404) {
|
|
68
|
+
// Genuinely absent (e.g. local dev, where no config was emitted).
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// Any other non-2xx (5xx, 401/403, etc.) is a real load failure, not
|
|
72
|
+
// absence. Treating it as "absent" would silently fall back to the
|
|
73
|
+
// build-time source-stage values, which in a deployed stage can mean
|
|
74
|
+
// sending users to the wrong backend. Fail closed instead.
|
|
75
|
+
throw new RayfinConfigError(`Failed to load Rayfin config from "${path}": ` +
|
|
76
|
+
`${response.status} ${response.statusText}.`, 'CONFIG_LOAD_FAILED');
|
|
77
|
+
}
|
|
78
|
+
const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
|
|
79
|
+
const text = await response.text();
|
|
80
|
+
const trimmed = text.trimStart();
|
|
81
|
+
// A missing /rayfin.config.json is not always a 404: Vite's dev server and
|
|
82
|
+
// SPA static hosts serve index.html (200) for unknown paths. An HTML response
|
|
83
|
+
// therefore means "no config file" (e.g. local dev, where `rayfin dev` relies
|
|
84
|
+
// on build-time VITE_* values) — treat it as absent rather than a malformed
|
|
85
|
+
// config. Check the declared content type first, then fall back to sniffing
|
|
86
|
+
// the body for hosts that mislabel the fallback.
|
|
87
|
+
if (contentType.includes('html') || trimmed.startsWith('<')) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
let json;
|
|
91
|
+
try {
|
|
92
|
+
json = JSON.parse(trimmed);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw new RayfinConfigError(`Rayfin config at "${path}" is not valid JSON.`, 'CONFIG_PARSE_FAILED');
|
|
96
|
+
}
|
|
97
|
+
return validateConfig(json, path);
|
|
98
|
+
}
|
|
99
|
+
function validateConfig(json, path) {
|
|
100
|
+
if (typeof json !== 'object' || json === null || Array.isArray(json)) {
|
|
101
|
+
throw new RayfinConfigError(`Rayfin config at "${path}" must be a JSON object.`, 'CONFIG_INVALID');
|
|
102
|
+
}
|
|
103
|
+
const obj = json;
|
|
104
|
+
const missing = [];
|
|
105
|
+
if (!obj.apiUrl || typeof obj.apiUrl !== 'string') {
|
|
106
|
+
missing.push('apiUrl');
|
|
107
|
+
}
|
|
108
|
+
if (missing.length > 0) {
|
|
109
|
+
throw new RayfinConfigError(`Rayfin config at "${path}" is incomplete. ` +
|
|
110
|
+
`Missing required fields: ${missing.join(', ')}.`, 'CONFIG_INCOMPLETE');
|
|
111
|
+
}
|
|
112
|
+
const rawApiUrl = obj.apiUrl;
|
|
113
|
+
const publishableKey = readOptionalStringField(obj, 'publishableKey', path);
|
|
114
|
+
const workspaceId = readOptionalStringField(obj, 'workspaceId', path);
|
|
115
|
+
const portalUrl = readOptionalStringField(obj, 'portalUrl', path);
|
|
116
|
+
const itemId = readOptionalStringField(obj, 'itemId', path);
|
|
117
|
+
const tenantId = readOptionalStringField(obj, 'tenantId', path);
|
|
118
|
+
const config = {
|
|
119
|
+
apiUrl: rawApiUrl.endsWith('/') ? rawApiUrl : `${rawApiUrl}/`,
|
|
120
|
+
...(publishableKey ? { publishableKey } : {}),
|
|
121
|
+
};
|
|
122
|
+
if (workspaceId) {
|
|
123
|
+
config.workspaceId = workspaceId;
|
|
124
|
+
}
|
|
125
|
+
if (portalUrl) {
|
|
126
|
+
config.portalUrl = portalUrl;
|
|
127
|
+
}
|
|
128
|
+
if (itemId) {
|
|
129
|
+
config.itemId = itemId;
|
|
130
|
+
}
|
|
131
|
+
if (tenantId) {
|
|
132
|
+
config.tenantId = tenantId;
|
|
133
|
+
}
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Reads an optional string field from a parsed config object.
|
|
138
|
+
*
|
|
139
|
+
* A missing, `null`, or empty-string value is treated as absent
|
|
140
|
+
* (`undefined`). A present value of any other type is a malformed config —
|
|
141
|
+
* rather than silently dropping it via an unchecked cast, this throws so the
|
|
142
|
+
* caller finds out immediately instead of the SDK crashing later with an
|
|
143
|
+
* untyped error (e.g. a numeric `publishableKey` reaching `.trim()`).
|
|
144
|
+
*/
|
|
145
|
+
function readOptionalStringField(obj, field, path) {
|
|
146
|
+
const value = obj[field];
|
|
147
|
+
if (value === undefined || value === null || value === '') {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
if (typeof value !== 'string') {
|
|
151
|
+
throw new RayfinConfigError(`Rayfin config at "${path}" has an invalid "${field}" field: ` +
|
|
152
|
+
`expected a string.`, 'CONFIG_INVALID');
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Resolves the config a `RayfinClient` / `RayfinServerClient` should be
|
|
158
|
+
* constructed with: loads the remote `rayfin.config.json` (unless the caller
|
|
159
|
+
* skips calling this function entirely), overlays it over the supplied
|
|
160
|
+
* `defaults` per-field, and returns the merged `baseUrl` / `publishableKey` /
|
|
161
|
+
* `runtimeConfig`. Construction itself is a plain, synchronous
|
|
162
|
+
* `new RayfinClient(...)` call — this function only resolves values.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* import { RayfinClient, resolveRayfinConfig } from '@microsoft/rayfin-client';
|
|
167
|
+
*
|
|
168
|
+
* const resolved = await resolveRayfinConfig({ baseUrl, publishableKey });
|
|
169
|
+
* const client = new RayfinClient({ ...resolved, authStorage: true });
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export async function resolveRayfinConfig(defaults, options = {}) {
|
|
173
|
+
const remote = await loadRayfinConfig(options.configUrl);
|
|
174
|
+
const baseUrl = remote?.apiUrl ?? defaults.apiUrl;
|
|
175
|
+
const publishableKey = remote?.publishableKey ?? defaults.publishableKey;
|
|
176
|
+
return {
|
|
177
|
+
baseUrl,
|
|
178
|
+
publishableKey,
|
|
179
|
+
runtimeConfig: {
|
|
180
|
+
apiUrl: baseUrl,
|
|
181
|
+
publishableKey,
|
|
182
|
+
workspaceId: remote?.workspaceId ?? defaults.workspaceId,
|
|
183
|
+
itemId: remote?.itemId ?? defaults.itemId,
|
|
184
|
+
portalUrl: remote?.portalUrl ?? defaults.portalUrl,
|
|
185
|
+
tenantId: remote?.tenantId ?? defaults.tenantId,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=config.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './client.js';
|
|
2
2
|
export { default } from './client.js';
|
|
3
|
+
export { RayfinConfigError, resolveRayfinConfig } from './config.js';
|
|
4
|
+
export type { RayfinRuntimeConfig, ResolveRayfinConfigOptions } from './config.js';
|
|
3
5
|
export type { TypedDataClients, EntitySchema } from '@microsoft/rayfin-data';
|
|
4
6
|
export type { FunctionsSchema } from '@microsoft/rayfin-functions';
|
|
5
7
|
export type { ApiClientConfig } from '@microsoft/rayfin-lib';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Export all from client.ts
|
|
2
2
|
export * from './client.js';
|
|
3
3
|
export { default } from './client.js';
|
|
4
|
+
// Runtime configuration loading
|
|
5
|
+
export { RayfinConfigError, resolveRayfinConfig } from './config.js';
|
|
4
6
|
// Re-export deprecation silencing controls from rayfin-lib so consumers have a
|
|
5
7
|
// stable public import path here; emitter helpers stay internal to the SDK.
|
|
6
8
|
export { setDeprecationsSilenced, isDeprecationSilenced, } from '@microsoft/rayfin-lib';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/rayfin-client",
|
|
3
|
-
"version": "1.35.0-
|
|
3
|
+
"version": "1.35.0-beta.0",
|
|
4
4
|
"description": "Main client SDK for Rayfin services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"default": "./dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"./experimental": {
|
|
12
|
+
"typedoc": null,
|
|
12
13
|
"types": "./dist/experimental/index.d.ts",
|
|
13
14
|
"default": "./dist/experimental/index.js"
|
|
14
15
|
}
|
|
@@ -21,11 +22,11 @@
|
|
|
21
22
|
"assets/docs"
|
|
22
23
|
],
|
|
23
24
|
"dependencies": {
|
|
24
|
-
"@microsoft/rayfin-auth": "1.35.0-
|
|
25
|
-
"@microsoft/rayfin-connectors": "1.35.0-
|
|
26
|
-
"@microsoft/rayfin-
|
|
27
|
-
"@microsoft/rayfin-
|
|
28
|
-
"@microsoft/rayfin-functions": "1.35.0-
|
|
25
|
+
"@microsoft/rayfin-auth": "1.35.0-beta.0",
|
|
26
|
+
"@microsoft/rayfin-connectors": "1.35.0-beta.0",
|
|
27
|
+
"@microsoft/rayfin-data": "1.35.0-beta.0",
|
|
28
|
+
"@microsoft/rayfin-lib": "1.35.0-beta.0",
|
|
29
|
+
"@microsoft/rayfin-functions": "1.35.0-beta.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"typescript": "^5.8.3",
|