@jaypie/fabric 0.2.0 → 0.2.2
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/cjs/ServiceSuite.d.ts +3 -1
- package/dist/cjs/commander/index.cjs +42 -11
- package/dist/cjs/commander/index.cjs.map +1 -1
- package/dist/cjs/data/index.cjs +40 -11
- package/dist/cjs/data/index.cjs.map +1 -1
- package/dist/cjs/http/index.cjs +43 -13
- package/dist/cjs/http/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +50 -21
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/lambda/index.cjs +42 -11
- package/dist/cjs/lambda/index.cjs.map +1 -1
- package/dist/cjs/llm/index.cjs +42 -11
- package/dist/cjs/llm/index.cjs.map +1 -1
- package/dist/cjs/mcp/FabricMcpServer.d.ts +1 -1
- package/dist/cjs/mcp/index.cjs +43 -12
- package/dist/cjs/mcp/index.cjs.map +1 -1
- package/dist/cjs/models/base.d.ts +6 -6
- package/dist/cjs/resolveService.d.ts +7 -4
- package/dist/cjs/service.d.ts +6 -4
- package/dist/cjs/types.d.ts +9 -3
- package/dist/cjs/websocket/fabricWebSocket.d.ts +120 -0
- package/dist/cjs/websocket/index.d.ts +2 -0
- package/dist/esm/ServiceSuite.d.ts +3 -1
- package/dist/esm/commander/index.js +42 -11
- package/dist/esm/commander/index.js.map +1 -1
- package/dist/esm/data/index.js +40 -11
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/http/index.js +43 -13
- package/dist/esm/http/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +50 -21
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lambda/index.js +42 -11
- package/dist/esm/lambda/index.js.map +1 -1
- package/dist/esm/llm/index.js +42 -11
- package/dist/esm/llm/index.js.map +1 -1
- package/dist/esm/mcp/FabricMcpServer.d.ts +1 -1
- package/dist/esm/mcp/index.js +43 -12
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/models/base.d.ts +6 -6
- package/dist/esm/resolveService.d.ts +7 -4
- package/dist/esm/service.d.ts +6 -4
- package/dist/esm/types.d.ts +9 -3
- package/dist/esm/websocket/fabricWebSocket.d.ts +120 -0
- package/dist/esm/websocket/index.d.ts +2 -0
- package/package.json +6 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { BroadcastResult, SendResult, WebSocketContext, WebSocketEvent, WebSocketResponse } from "@jaypie/lambda";
|
|
2
|
+
import type { InputFieldDefinition, Message, Service, ServiceContext, ServiceFunction } from "../types.js";
|
|
3
|
+
/** Callback called when handler completes successfully */
|
|
4
|
+
export type OnCompleteCallback = (response: unknown) => void | Promise<void>;
|
|
5
|
+
/** Callback called for recoverable errors (via context.onError) */
|
|
6
|
+
export type OnErrorCallback = (error: unknown) => void | Promise<void>;
|
|
7
|
+
/** Callback called for fatal errors (thrown or via context.onFatal) */
|
|
8
|
+
export type OnFatalCallback = (error: unknown) => void | Promise<void>;
|
|
9
|
+
/** Callback for receiving messages from service during execution */
|
|
10
|
+
export type OnMessageCallback = (message: Message) => void | Promise<void>;
|
|
11
|
+
/** Callback for $connect event */
|
|
12
|
+
export type OnConnectCallback = (context: WebSocketContext) => void | Promise<void>;
|
|
13
|
+
/** Callback for $disconnect event */
|
|
14
|
+
export type OnDisconnectCallback = (context: WebSocketContext) => void | Promise<void>;
|
|
15
|
+
type LifecycleFunction = (...args: unknown[]) => void | Promise<void>;
|
|
16
|
+
type ValidatorFunction = (...args: unknown[]) => unknown | Promise<unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Extended service context for WebSocket handlers
|
|
19
|
+
*/
|
|
20
|
+
export interface WebSocketServiceContext extends ServiceContext {
|
|
21
|
+
/** Broadcast data to multiple connections */
|
|
22
|
+
broadcast: (connectionIds: string[], data: unknown) => Promise<BroadcastResult>;
|
|
23
|
+
/** The WebSocket connection ID */
|
|
24
|
+
connectionId: string;
|
|
25
|
+
/** The WebSocket API domain name */
|
|
26
|
+
domainName: string;
|
|
27
|
+
/** Query string parameters from $connect */
|
|
28
|
+
queryStringParameters: Record<string, string> | null;
|
|
29
|
+
/** The route key (e.g., "$connect", "$disconnect", "$default", or custom) */
|
|
30
|
+
routeKey: string;
|
|
31
|
+
/** Send data to the current connection */
|
|
32
|
+
send: (data: unknown) => Promise<SendResult>;
|
|
33
|
+
/** The WebSocket API stage */
|
|
34
|
+
stage: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Options for fabricWebSocket
|
|
38
|
+
*/
|
|
39
|
+
export interface FabricWebSocketOptions {
|
|
40
|
+
/** Chaos testing mode */
|
|
41
|
+
chaos?: string;
|
|
42
|
+
/** Override the service name for logging (defaults to service.alias) */
|
|
43
|
+
name?: string;
|
|
44
|
+
/** Callback for $connect events */
|
|
45
|
+
onConnect?: OnConnectCallback;
|
|
46
|
+
/** Callback called when handler completes successfully */
|
|
47
|
+
onComplete?: OnCompleteCallback;
|
|
48
|
+
/** Callback for $disconnect events */
|
|
49
|
+
onDisconnect?: OnDisconnectCallback;
|
|
50
|
+
/** Callback for recoverable errors (via context.onError) */
|
|
51
|
+
onError?: OnErrorCallback;
|
|
52
|
+
/** Callback for fatal errors (thrown or via context.onFatal) */
|
|
53
|
+
onFatal?: OnFatalCallback;
|
|
54
|
+
/** Callback for receiving messages from service during execution */
|
|
55
|
+
onMessage?: OnMessageCallback;
|
|
56
|
+
/** AWS secrets to load into process.env */
|
|
57
|
+
secrets?: string[];
|
|
58
|
+
/** Functions to run before handler */
|
|
59
|
+
setup?: LifecycleFunction[];
|
|
60
|
+
/** Functions to run after handler (always runs) */
|
|
61
|
+
teardown?: LifecycleFunction[];
|
|
62
|
+
/** Re-throw errors instead of returning error response */
|
|
63
|
+
throw?: boolean;
|
|
64
|
+
/** Return 503 Unavailable immediately */
|
|
65
|
+
unavailable?: boolean;
|
|
66
|
+
/** Validation functions to run before handler */
|
|
67
|
+
validate?: ValidatorFunction[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Configuration for fabricWebSocket
|
|
71
|
+
*/
|
|
72
|
+
export interface FabricWebSocketConfig extends FabricWebSocketOptions {
|
|
73
|
+
/** Service alias (used as name for logging if `name` not provided) */
|
|
74
|
+
alias?: string;
|
|
75
|
+
/** Service description */
|
|
76
|
+
description?: string;
|
|
77
|
+
/** Input field definitions */
|
|
78
|
+
input?: Record<string, InputFieldDefinition>;
|
|
79
|
+
/** The service - either a pre-instantiated Service or an inline function */
|
|
80
|
+
service: Service | ServiceFunction<Record<string, unknown>, unknown>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The returned WebSocket Lambda handler function
|
|
84
|
+
*/
|
|
85
|
+
export type FabricWebSocketResult = (event: WebSocketEvent, context?: {
|
|
86
|
+
awsRequestId?: string;
|
|
87
|
+
[key: string]: unknown;
|
|
88
|
+
}, ...args: unknown[]) => Promise<WebSocketResponse>;
|
|
89
|
+
/**
|
|
90
|
+
* Fabric a WebSocket Lambda handler that wraps a service.
|
|
91
|
+
*
|
|
92
|
+
* This function creates a WebSocket-compatible Lambda handler that:
|
|
93
|
+
* - Parses the WebSocket body as service input
|
|
94
|
+
* - Provides WebSocket context (connectionId, send, broadcast) to the service
|
|
95
|
+
* - Handles $connect and $disconnect events with optional callbacks
|
|
96
|
+
* - Integrates with websocketHandler for lifecycle management
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import { fabricWebSocket } from "@jaypie/fabric/websocket";
|
|
101
|
+
* import { myService } from "./services";
|
|
102
|
+
*
|
|
103
|
+
* // Direct service style
|
|
104
|
+
* export const handler = fabricWebSocket(myService);
|
|
105
|
+
*
|
|
106
|
+
* // Config object style with lifecycle hooks
|
|
107
|
+
* export const handler2 = fabricWebSocket({
|
|
108
|
+
* service: myService,
|
|
109
|
+
* secrets: ["MONGODB_URI"],
|
|
110
|
+
* onConnect: async (context) => {
|
|
111
|
+
* await storeConnection(context.connectionId);
|
|
112
|
+
* },
|
|
113
|
+
* onDisconnect: async (context) => {
|
|
114
|
+
* await removeConnection(context.connectionId);
|
|
115
|
+
* },
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function fabricWebSocket(serviceOrConfig: FabricWebSocketConfig | Service | ServiceFunction<Record<string, unknown>, unknown>, options?: FabricWebSocketOptions): FabricWebSocketResult;
|
|
120
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { fabricWebSocket } from "./fabricWebSocket.js";
|
|
2
|
+
export type { FabricWebSocketConfig, FabricWebSocketOptions, FabricWebSocketResult, OnCompleteCallback, OnConnectCallback, OnDisconnectCallback, OnErrorCallback, OnFatalCallback, OnMessageCallback, WebSocketServiceContext, } from "./fabricWebSocket.js";
|
|
@@ -43,7 +43,9 @@ export interface ServiceSuite {
|
|
|
43
43
|
/** Execute a service by name */
|
|
44
44
|
execute(name: string, inputs: Record<string, unknown>): Promise<unknown>;
|
|
45
45
|
/** Register a fabricService into the suite */
|
|
46
|
-
register(service: Service<any, any>,
|
|
46
|
+
register(service: Service<any, any>, options: {
|
|
47
|
+
category: string;
|
|
48
|
+
}): void;
|
|
47
49
|
/** Get all registered service functions (for transport adapters like FabricMcpServer) */
|
|
48
50
|
getServiceFunctions(): Service<any, any>[];
|
|
49
51
|
/** Get a specific service function by name */
|
|
@@ -856,42 +856,68 @@ async function processField(fieldName, value, definition) {
|
|
|
856
856
|
function isService(value) {
|
|
857
857
|
return typeof value === "function" && "$fabric" in value;
|
|
858
858
|
}
|
|
859
|
+
/**
|
|
860
|
+
* Run serializer hook if provided
|
|
861
|
+
* Returns transformed output or original if serializer returns undefined/null/void
|
|
862
|
+
*/
|
|
863
|
+
async function runSerializer(data, serializer, context) {
|
|
864
|
+
if (!serializer) {
|
|
865
|
+
return data.output;
|
|
866
|
+
}
|
|
867
|
+
const result = await serializer(data, context);
|
|
868
|
+
if (result !== undefined && result !== null) {
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
871
|
+
return data.output;
|
|
872
|
+
}
|
|
859
873
|
/**
|
|
860
874
|
* Fabric a service function
|
|
861
875
|
*
|
|
862
|
-
* Service builds a function that
|
|
876
|
+
* Service builds a function that:
|
|
863
877
|
* - Parses the input if it is a string to object
|
|
864
878
|
* - Fabrics each input field to its type
|
|
865
879
|
* - Calls the validation function or regular expression or checks the array
|
|
866
|
-
* - Calls the service function
|
|
880
|
+
* - Calls the service function
|
|
881
|
+
* - Calls the serializer hook (can transform output)
|
|
882
|
+
* - Returns the response
|
|
867
883
|
*
|
|
868
884
|
* The returned function has config properties for introspection.
|
|
869
885
|
*/
|
|
870
886
|
function fabricService(config) {
|
|
871
|
-
const { input: inputDefinitions, service } = config;
|
|
887
|
+
const { input: inputDefinitions, serializer, service } = config;
|
|
872
888
|
const handler = async (rawInput, context) => {
|
|
873
889
|
// Parse input (handles string JSON)
|
|
874
890
|
const parsedInput = parseInput(rawInput);
|
|
875
891
|
// If no input definitions, pass through to service or return parsed input
|
|
876
892
|
if (!inputDefinitions) {
|
|
893
|
+
let output;
|
|
877
894
|
if (service) {
|
|
878
|
-
|
|
895
|
+
output = await service(parsedInput, context);
|
|
879
896
|
}
|
|
880
|
-
|
|
897
|
+
else {
|
|
898
|
+
output = parsedInput;
|
|
899
|
+
}
|
|
900
|
+
// Run serializer
|
|
901
|
+
return (await runSerializer({ input: parsedInput, output }, serializer, context));
|
|
881
902
|
}
|
|
882
903
|
// Process all fields in parallel
|
|
883
904
|
const entries = Object.entries(inputDefinitions);
|
|
884
905
|
const processedValues = await Promise.all(entries.map(([fieldName, definition]) => processField(fieldName, parsedInput[fieldName], definition)));
|
|
885
906
|
// Build processed input object
|
|
886
|
-
const
|
|
907
|
+
const processedInputObj = {};
|
|
887
908
|
entries.forEach(([fieldName], index) => {
|
|
888
|
-
|
|
909
|
+
processedInputObj[fieldName] = processedValues[index];
|
|
889
910
|
});
|
|
890
|
-
//
|
|
911
|
+
// Call service or return processed input
|
|
912
|
+
let output;
|
|
891
913
|
if (service) {
|
|
892
|
-
|
|
914
|
+
output = await service(processedInputObj, context);
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
output = processedInputObj;
|
|
893
918
|
}
|
|
894
|
-
|
|
919
|
+
// Run serializer hook
|
|
920
|
+
return (await runSerializer({ input: processedInputObj, output }, serializer, context));
|
|
895
921
|
};
|
|
896
922
|
// Attach config properties directly to handler for flat access
|
|
897
923
|
const typedHandler = handler;
|
|
@@ -902,6 +928,8 @@ function fabricService(config) {
|
|
|
902
928
|
typedHandler.description = config.description;
|
|
903
929
|
if (config.input !== undefined)
|
|
904
930
|
typedHandler.input = config.input;
|
|
931
|
+
if (config.serializer !== undefined)
|
|
932
|
+
typedHandler.serializer = config.serializer;
|
|
905
933
|
if (config.service !== undefined)
|
|
906
934
|
typedHandler.service = config.service;
|
|
907
935
|
return typedHandler;
|
|
@@ -920,6 +948,7 @@ function fabricService(config) {
|
|
|
920
948
|
* - `alias` overrides service.alias
|
|
921
949
|
* - `description` overrides service.description
|
|
922
950
|
* - `input` overrides service.input
|
|
951
|
+
* - `serializer` overrides service.serializer
|
|
923
952
|
*
|
|
924
953
|
* The original Service is never mutated - a new Service is created when overrides
|
|
925
954
|
* are applied.
|
|
@@ -943,7 +972,7 @@ function fabricService(config) {
|
|
|
943
972
|
* ```
|
|
944
973
|
*/
|
|
945
974
|
function resolveService(config) {
|
|
946
|
-
const { alias, description, input, service } = config;
|
|
975
|
+
const { alias, description, input, serializer, service } = config;
|
|
947
976
|
if (isService(service)) {
|
|
948
977
|
// Service is pre-instantiated - config fields act as overrides
|
|
949
978
|
// Create new Service with merged properties (config overrides service)
|
|
@@ -951,6 +980,7 @@ function resolveService(config) {
|
|
|
951
980
|
alias: alias ?? service.alias,
|
|
952
981
|
description: description ?? service.description,
|
|
953
982
|
input: input ?? service.input,
|
|
983
|
+
serializer: serializer ?? service.serializer,
|
|
954
984
|
service: service.service,
|
|
955
985
|
});
|
|
956
986
|
}
|
|
@@ -959,6 +989,7 @@ function resolveService(config) {
|
|
|
959
989
|
alias,
|
|
960
990
|
description,
|
|
961
991
|
input,
|
|
992
|
+
serializer,
|
|
962
993
|
service,
|
|
963
994
|
});
|
|
964
995
|
}
|