@microsoft/rayfin-functions 1.28.0 → 1.29.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.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @fileoverview Per-function typed client.
3
+ *
4
+ * Each property on the proxy returned by `createFunctionsApi` is a
5
+ * `FunctionClient` whose `invoke()` signature is derived from the schema
6
+ * entry for that function name.
7
+ */
8
+ import { ApiClient } from '@microsoft/rayfin-lib';
9
+ /**
10
+ * Response from a function invocation.
11
+ */
12
+ export interface FunctionInvocationResponse<TOutput = any> {
13
+ /** The name of the function that was invoked. */
14
+ functionName: string;
15
+ /** A unique identifier for this invocation. */
16
+ invocationId: string;
17
+ /** Status of the function invocation (Success, Failed, etc.) */
18
+ status: string;
19
+ /**
20
+ * The output from the function.
21
+ * When the raw response contains a JSON-encoded string, `invoke()` auto-parses it
22
+ * so the caller receives `TOutput` directly.
23
+ */
24
+ output: TOutput;
25
+ /** Any errors that occurred during the function invocation. */
26
+ errors: Array<string | Record<string, any>>;
27
+ }
28
+ /**
29
+ * Options that can be supplied to a single `invoke()` call.
30
+ */
31
+ export interface InvokeOptions {
32
+ /** Extra headers to attach to the request. */
33
+ headers?: Record<string, string>;
34
+ }
35
+ /**
36
+ * A strongly-typed client for a single function.
37
+ *
38
+ * @template TInput - The parameter object the function expects (`void` when none).
39
+ * @template TOutput - The type returned by the function.
40
+ */
41
+ export declare class FunctionClient<TInput = any, TOutput = any> {
42
+ private apiClient;
43
+ private functionName;
44
+ constructor(apiClient: ApiClient, functionName: string);
45
+ /**
46
+ * Invoke the function.
47
+ *
48
+ * @param params - Input parameters (omit when the function takes no input).
49
+ * @param options - Optional per-call settings (extra headers, etc.).
50
+ * @returns A response whose `output` field is typed as `TOutput`.
51
+ *
52
+ * @throws {FunctionsError} If the function invocation fails.
53
+ * @throws {NetworkError} For network-related issues.
54
+ * @throws {SdkError} For any other unexpected SDK errors.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const res = await client.functions.helloWorld.invoke({
59
+ * firstName: 'Ada',
60
+ * lastName: 'Lovelace',
61
+ * });
62
+ * console.log(res.output); // typed as string
63
+ * ```
64
+ */
65
+ invoke(...args: TInput extends void ? [options?: InvokeOptions] : [params: TInput, options?: InvokeOptions]): Promise<FunctionInvocationResponse<TOutput>>;
66
+ }
67
+ //# sourceMappingURL=FunctionClient.d.ts.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @fileoverview Per-function typed client.
3
+ *
4
+ * Each property on the proxy returned by `createFunctionsApi` is a
5
+ * `FunctionClient` whose `invoke()` signature is derived from the schema
6
+ * entry for that function name.
7
+ */
8
+ import { SdkError, NetworkError } from '@microsoft/rayfin-lib';
9
+ import { FUNCTIONS_BASE_PATH } from '@microsoft/rayfin-lib';
10
+ import { FunctionsError } from './Functions';
11
+ /**
12
+ * A strongly-typed client for a single function.
13
+ *
14
+ * @template TInput - The parameter object the function expects (`void` when none).
15
+ * @template TOutput - The type returned by the function.
16
+ */
17
+ export class FunctionClient {
18
+ apiClient;
19
+ functionName;
20
+ constructor(apiClient, functionName) {
21
+ this.apiClient = apiClient;
22
+ this.functionName = functionName;
23
+ }
24
+ /**
25
+ * Invoke the function.
26
+ *
27
+ * @param params - Input parameters (omit when the function takes no input).
28
+ * @param options - Optional per-call settings (extra headers, etc.).
29
+ * @returns A response whose `output` field is typed as `TOutput`.
30
+ *
31
+ * @throws {FunctionsError} If the function invocation fails.
32
+ * @throws {NetworkError} For network-related issues.
33
+ * @throws {SdkError} For any other unexpected SDK errors.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const res = await client.functions.helloWorld.invoke({
38
+ * firstName: 'Ada',
39
+ * lastName: 'Lovelace',
40
+ * });
41
+ * console.log(res.output); // typed as string
42
+ * ```
43
+ */
44
+ async invoke(...args) {
45
+ try {
46
+ // Unpack the variadic args – when TInput is void the first arg is options.
47
+ let parameters;
48
+ let options;
49
+ if (args.length === 0) {
50
+ // No-arg call: `fn.invoke()`
51
+ }
52
+ else if (args.length === 1) {
53
+ // Could be `invoke(params)` or `invoke(options)` for void-input fns
54
+ const first = args[0];
55
+ if (first &&
56
+ typeof first === 'object' &&
57
+ 'headers' in first &&
58
+ Object.keys(first).every((k) => k === 'headers')) {
59
+ options = first;
60
+ }
61
+ else {
62
+ parameters = first;
63
+ }
64
+ }
65
+ else {
66
+ parameters = args[0];
67
+ options = args[1];
68
+ }
69
+ const url = `${FUNCTIONS_BASE_PATH}/${this.functionName}/invoke`;
70
+ const response = await this.apiClient.post(url, parameters ?? {}, { headers: options?.headers });
71
+ // Check for errors in the response body
72
+ if (response.errors && response.errors.length > 0) {
73
+ const errorMessage = typeof response.errors[0] === 'string'
74
+ ? response.errors[0]
75
+ : JSON.stringify(response.errors[0]);
76
+ throw new FunctionsError(`Function invocation failed: ${errorMessage}`, 'FUNCTION_EXECUTION_ERROR');
77
+ }
78
+ // Check for failed status
79
+ const status = response.status.toLowerCase();
80
+ if (status !== 'success' && status !== 'succeeded') {
81
+ throw new FunctionsError(`Function invocation failed with status: ${response.status}`, 'FUNCTION_EXECUTION_ERROR');
82
+ }
83
+ // Auto-parse JSON-encoded output strings
84
+ if (typeof response.output === 'string') {
85
+ try {
86
+ const parsed = JSON.parse(response.output);
87
+ // If the parsed envelope has its own `output` field, unwrap it
88
+ if (parsed && typeof parsed === 'object' && 'output' in parsed) {
89
+ response.output = parsed.output;
90
+ }
91
+ else {
92
+ response.output = parsed;
93
+ }
94
+ }
95
+ catch {
96
+ // Not JSON — leave as-is (TOutput may be `string`)
97
+ }
98
+ }
99
+ return response;
100
+ }
101
+ catch (error) {
102
+ if (error instanceof FunctionsError ||
103
+ error instanceof NetworkError ||
104
+ error instanceof SdkError) {
105
+ throw error;
106
+ }
107
+ throw new FunctionsError(`An unexpected error occurred during function invocation: ${error.message || error}`, 'UNKNOWN_FUNCTION_ERROR');
108
+ }
109
+ }
110
+ }
111
+ //# sourceMappingURL=FunctionClient.js.map
@@ -1,8 +1,19 @@
1
1
  /**
2
- * @fileoverview Functions API client for invoking serverless functions.
3
- * This file provides an API for invoking serverless functions through the Rayfin platform.
2
+ * @fileoverview Functions API for invoking serverless functions.
3
+ *
4
+ * The single public surface is `client.functions.<name>.invoke(...)` where
5
+ * `<name>` is constrained by the `FunctionsSchema` type parameter passed to
6
+ * `RayfinClient`.
7
+ *
8
+ * ```ts
9
+ * const res = await client.functions.helloWorld.invoke({ firstName: 'Ada' });
10
+ * ```
4
11
  */
5
- import { ApiClient, SdkError, NetworkError } from '@microsoft/rayfin-lib';
12
+ import { ApiClient, SdkError } from '@microsoft/rayfin-lib';
13
+ import { FunctionClient } from './FunctionClient';
14
+ import type { FunctionsSchema } from './FunctionsSchema';
15
+ export { FunctionClient } from './FunctionClient';
16
+ export type { FunctionInvocationResponse, InvokeOptions, } from './FunctionClient';
6
17
  /**
7
18
  * Functions error specific to the Rayfin SDK.
8
19
  */
@@ -10,99 +21,23 @@ export declare class FunctionsError extends SdkError {
10
21
  constructor(message: string, code?: string);
11
22
  }
12
23
  /**
13
- * Parameters for a function invocation request.
24
+ * Mapped type that produces one `FunctionClient` property per schema entry.
25
+ *
26
+ * `client.functions` resolves to this type — every key in `TSchema` becomes
27
+ * a strongly-typed per-function client whose `invoke()` signature matches
28
+ * the schema entry.
14
29
  */
15
- export interface FunctionInvocationParams {
16
- /**
17
- * The name of the function to invoke.
18
- */
19
- functionName: string;
20
- /**
21
- * Optional parameters to pass to the function.
22
- */
23
- parameters?: Record<string, any>;
24
- /**
25
- * Optional headers to include with the invocation request.
26
- */
27
- headers?: Record<string, string>;
28
- }
30
+ export type TypedFunctionClients<TSchema extends FunctionsSchema> = {
31
+ [K in keyof TSchema & string]: FunctionClient<TSchema[K]['input'], TSchema[K]['output']>;
32
+ };
29
33
  /**
30
- * Response from a function invocation.
34
+ * Create a typed `client.functions` proxy that lazily instantiates and caches
35
+ * a {@link FunctionClient} per schema-defined function name.
36
+ *
37
+ * ```ts
38
+ * const fns = createFunctionsApi<MyFunctionsSchema>(apiClient);
39
+ * const res = await fns.helloWorld.invoke({ firstName: 'Ada' });
40
+ * ```
31
41
  */
32
- export interface FunctionInvocationResponse<T = any> {
33
- /**
34
- * The name of the function that was invoked.
35
- */
36
- functionName: string;
37
- /**
38
- * A unique identifier for this invocation.
39
- */
40
- invocationId: string;
41
- /**
42
- * Status of the function invocation (Success, Failed, etc.)
43
- */
44
- status: string;
45
- /**
46
- * The output from the function. This can be a string containing JSON or any other data.
47
- * When it's a JSON string, it will typically have its own nested structure with functionName,
48
- * invocationId, status, output, and errors fields.
49
- */
50
- output: string | T;
51
- /**
52
- * Any errors that occurred during the function invocation.
53
- */
54
- errors: Array<string | Record<string, any>>;
55
- }
56
- /**
57
- * Main Functions class for Rayfin.
58
- * This class provides the high-level API for invoking serverless functions.
59
- */
60
- export declare class FunctionsApi {
61
- private apiClient;
62
- static readonly errors: {
63
- FunctionsError: typeof FunctionsError;
64
- NetworkError: typeof NetworkError;
65
- SdkError: typeof SdkError;
66
- };
67
- /**
68
- * @param apiClient An instance of ApiClient to be used for API requests.
69
- */
70
- constructor(apiClient: ApiClient);
71
- /**
72
- * Helper method to parse the output field of a function response when it contains a JSON string.
73
- *
74
- * @param response The function invocation response
75
- * @returns The parsed output object, or the original output if it's not valid JSON
76
- */
77
- parseOutput<T = any, R = any>(response: FunctionInvocationResponse<T>): R | string | T;
78
- /**
79
- * Invokes a serverless function with the given parameters.
80
- *
81
- * @param params The function invocation parameters
82
- * @returns A promise that resolves with the function invocation response
83
- * @throws {FunctionsError} If the function invocation fails
84
- * @throws {NetworkError} For network-related issues
85
- * @throws {SdkError} For any other unexpected SDK errors
86
- *
87
- * @example
88
- * ```typescript
89
- * const response = await functions.invoke({
90
- * functionName: 'processOrder',
91
- * parameters: {
92
- * orderId: '12345',
93
- * amount: 99.99
94
- * }
95
- * });
96
- *
97
- * // Direct access to the response
98
- * console.log(response.status);
99
- * console.log(response.invocationId);
100
- *
101
- * // Parse the output field if it contains JSON
102
- * const parsedOutput = functions.parseOutput(response);
103
- * console.log(parsedOutput); // Will contain the actual result
104
- * ```
105
- */
106
- invoke<T = any>(params: FunctionInvocationParams): Promise<FunctionInvocationResponse<T>>;
107
- }
42
+ export declare function createFunctionsApi<TSchema extends FunctionsSchema = FunctionsSchema>(apiClient: ApiClient): TypedFunctionClients<TSchema>;
108
43
  //# sourceMappingURL=Functions.d.ts.map
package/dist/Functions.js CHANGED
@@ -1,9 +1,17 @@
1
1
  /**
2
- * @fileoverview Functions API client for invoking serverless functions.
3
- * This file provides an API for invoking serverless functions through the Rayfin platform.
2
+ * @fileoverview Functions API for invoking serverless functions.
3
+ *
4
+ * The single public surface is `client.functions.<name>.invoke(...)` where
5
+ * `<name>` is constrained by the `FunctionsSchema` type parameter passed to
6
+ * `RayfinClient`.
7
+ *
8
+ * ```ts
9
+ * const res = await client.functions.helloWorld.invoke({ firstName: 'Ada' });
10
+ * ```
4
11
  */
5
- import { SdkError, NetworkError } from '@microsoft/rayfin-lib';
6
- import { FUNCTIONS_INVOKE_PATH } from '@microsoft/rayfin-lib';
12
+ import { SdkError } from '@microsoft/rayfin-lib';
13
+ import { FunctionClient } from './FunctionClient';
14
+ export { FunctionClient } from './FunctionClient';
7
15
  /**
8
16
  * Functions error specific to the Rayfin SDK.
9
17
  */
@@ -13,117 +21,45 @@ export class FunctionsError extends SdkError {
13
21
  }
14
22
  }
15
23
  /**
16
- * Main Functions class for Rayfin.
17
- * This class provides the high-level API for invoking serverless functions.
24
+ * Create a typed `client.functions` proxy that lazily instantiates and caches
25
+ * a {@link FunctionClient} per schema-defined function name.
26
+ *
27
+ * ```ts
28
+ * const fns = createFunctionsApi<MyFunctionsSchema>(apiClient);
29
+ * const res = await fns.helloWorld.invoke({ firstName: 'Ada' });
30
+ * ```
18
31
  */
19
- export class FunctionsApi {
20
- apiClient;
21
- // Static access to custom errors for easy import by consumers
22
- static errors = {
23
- FunctionsError,
24
- NetworkError,
25
- SdkError,
26
- };
27
- /**
28
- * @param apiClient An instance of ApiClient to be used for API requests.
29
- */
30
- constructor(apiClient) {
31
- this.apiClient = apiClient;
32
- }
33
- /**
34
- * Helper method to parse the output field of a function response when it contains a JSON string.
35
- *
36
- * @param response The function invocation response
37
- * @returns The parsed output object, or the original output if it's not valid JSON
38
- */
39
- parseOutput(response) {
40
- if (typeof response.output === 'string') {
41
- try {
42
- // Try to parse the output as JSON
43
- const parsedOutput = JSON.parse(response.output);
44
- // If the parsed output has its own 'output' field, return that
45
- if (parsedOutput &&
46
- typeof parsedOutput === 'object' &&
47
- 'output' in parsedOutput) {
48
- return parsedOutput.output;
49
- }
50
- // Otherwise return the entire parsed object
51
- return parsedOutput;
52
- }
53
- catch (e) {
54
- // If parsing fails, return the original string
55
- return response.output;
56
- }
32
+ export function createFunctionsApi(apiClient) {
33
+ const cache = new Map();
34
+ const get = (name) => {
35
+ let client = cache.get(name);
36
+ if (!client) {
37
+ client = new FunctionClient(apiClient, name);
38
+ cache.set(name, client);
57
39
  }
58
- // If output is not a string, return it as is
59
- return response.output;
60
- }
61
- /**
62
- * Invokes a serverless function with the given parameters.
63
- *
64
- * @param params The function invocation parameters
65
- * @returns A promise that resolves with the function invocation response
66
- * @throws {FunctionsError} If the function invocation fails
67
- * @throws {NetworkError} For network-related issues
68
- * @throws {SdkError} For any other unexpected SDK errors
69
- *
70
- * @example
71
- * ```typescript
72
- * const response = await functions.invoke({
73
- * functionName: 'processOrder',
74
- * parameters: {
75
- * orderId: '12345',
76
- * amount: 99.99
77
- * }
78
- * });
79
- *
80
- * // Direct access to the response
81
- * console.log(response.status);
82
- * console.log(response.invocationId);
83
- *
84
- * // Parse the output field if it contains JSON
85
- * const parsedOutput = functions.parseOutput(response);
86
- * console.log(parsedOutput); // Will contain the actual result
87
- * ```
88
- */
89
- async invoke(params) {
90
- try {
91
- // Basic validation
92
- if (!params.functionName) {
93
- throw new FunctionsError('Function name is required for invocation', 'MISSING_FUNCTION_NAME');
94
- }
95
- const { functionName, parameters, headers } = params;
96
- // Prepare the request payload
97
- const payload = {
98
- functionName,
99
- parameters: parameters || {},
40
+ return client;
41
+ };
42
+ return new Proxy(Object.create(null), {
43
+ get(_target, prop) {
44
+ if (typeof prop !== 'string')
45
+ return undefined;
46
+ return get(prop);
47
+ },
48
+ has(_target, prop) {
49
+ return typeof prop === 'string';
50
+ },
51
+ ownKeys() {
52
+ return Array.from(cache.keys());
53
+ },
54
+ getOwnPropertyDescriptor(_target, prop) {
55
+ if (typeof prop !== 'string')
56
+ return undefined;
57
+ return {
58
+ enumerable: true,
59
+ configurable: true,
60
+ value: get(prop),
100
61
  };
101
- // Make the POST request to the functions/invoke endpoint
102
- const response = await this.apiClient.post(FUNCTIONS_INVOKE_PATH, payload, { headers });
103
- // Check for function errors in the response
104
- if (response.errors && response.errors.length > 0) {
105
- const errorMessage = typeof response.errors[0] === 'string'
106
- ? response.errors[0]
107
- : JSON.stringify(response.errors[0]);
108
- throw new FunctionsError(`Function invocation failed: ${errorMessage}`, 'FUNCTION_EXECUTION_ERROR');
109
- }
110
- // Check for failed status
111
- if (response.status.toLowerCase() !== 'success' &&
112
- response.status.toLowerCase() !== 'succeeded') {
113
- throw new FunctionsError(`Function invocation failed with status: ${response.status}`, 'FUNCTION_EXECUTION_ERROR');
114
- }
115
- return response;
116
- }
117
- catch (error) {
118
- // Re-throw specific SDK errors
119
- if (error instanceof FunctionsError ||
120
- error instanceof NetworkError ||
121
- error instanceof SdkError) {
122
- throw error;
123
- }
124
- // Wrap other errors in a FunctionsError
125
- throw new FunctionsError(`An unexpected error occurred during function invocation: ${error.message || error}`, 'UNKNOWN_FUNCTION_ERROR');
126
- }
127
- }
62
+ },
63
+ });
128
64
  }
129
65
  //# sourceMappingURL=Functions.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Maps function names to their input/output type pairs.
3
+ *
4
+ * Users define a concrete type that `satisfies FunctionsSchema` in their
5
+ * `rayfin/functions/src/types.ts` file, then pass it as the third type
6
+ * parameter of `RayfinClient` so that `client.functions.<name>.invoke()`
7
+ * calls are fully type-checked.
8
+ *
9
+ * Use an object type for named params, or `void` for no params.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import type { FunctionsSchema } from '@microsoft/rayfin-functions';
14
+ *
15
+ * export type MyFunctionsSchema = {
16
+ * helloWorld: { input: { firstName: string; lastName: string }; output: string };
17
+ * add: { input: { a: number; b: number }; output: number };
18
+ * } satisfies FunctionsSchema;
19
+ * ```
20
+ */
21
+ export type FunctionsSchema = Record<string, {
22
+ input: any;
23
+ output: any;
24
+ }>;
25
+ //# sourceMappingURL=FunctionsSchema.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FunctionsSchema.js.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
- export { FunctionsApi } from './Functions';
1
+ export { FunctionsError, createFunctionsApi, FunctionClient, } from './Functions';
2
+ export type { FunctionInvocationResponse, InvokeOptions, TypedFunctionClients, } from './Functions';
3
+ export type { FunctionsSchema } from './FunctionsSchema';
2
4
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { FunctionsApi } from './Functions';
1
+ export { FunctionsError, createFunctionsApi, FunctionClient, } from './Functions';
2
2
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/rayfin-functions",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "rimraf": "~6.0.1"
20
20
  },
21
21
  "dependencies": {
22
- "@microsoft/rayfin-lib": "1.28.0"
22
+ "@microsoft/rayfin-lib": "1.29.0"
23
23
  },
24
24
  "publishConfig": {
25
25
  "registry": "https://npm.pkg.github.com",