@forklaunch/core 0.15.12 → 0.16.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/lib/apiDefinition.types-BYizofKE.d.mts +1064 -0
- package/lib/apiDefinition.types-BYizofKE.d.ts +1064 -0
- package/lib/http/index.d.mts +14 -1055
- package/lib/http/index.d.ts +14 -1055
- package/lib/http/index.js +3 -2
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +3 -2
- package/lib/http/index.mjs.map +1 -1
- package/lib/ws/index.d.mts +65 -0
- package/lib/ws/index.d.ts +65 -0
- package/lib/ws/index.js +254 -0
- package/lib/ws/index.js.map +1 -0
- package/lib/ws/index.mjs +223 -0
- package/lib/ws/index.mjs.map +1 -0
- package/package.json +9 -2
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AsyncAPIObject } from '@asyncapi/parser/esm/spec-types/v3';
|
|
2
|
+
import { AnySchemaValidator, IdiomaticSchema, SchemaValidator } from '@forklaunch/validator';
|
|
3
|
+
import { S as StringOnlyObject } from '../apiDefinition.types-BYizofKE.mjs';
|
|
4
|
+
import '@forklaunch/common';
|
|
5
|
+
import '@opentelemetry/api';
|
|
6
|
+
import 'jose';
|
|
7
|
+
import 'qs';
|
|
8
|
+
import 'stream';
|
|
9
|
+
import 'pino';
|
|
10
|
+
import '@forklaunch/fastmcp-fork';
|
|
11
|
+
import 'cors';
|
|
12
|
+
import 'http';
|
|
13
|
+
import '@scalar/express-api-reference';
|
|
14
|
+
|
|
15
|
+
type AsyncApiEnrichment = {
|
|
16
|
+
channel?: string;
|
|
17
|
+
channels?: string[];
|
|
18
|
+
operation?: string;
|
|
19
|
+
operations?: string[];
|
|
20
|
+
};
|
|
21
|
+
type EventSchemaEntry<SV extends AnySchemaValidator> = {
|
|
22
|
+
shape: IdiomaticSchema<SV>;
|
|
23
|
+
} & AsyncApiEnrichment;
|
|
24
|
+
type EventSchema<SV extends AnySchemaValidator> = {
|
|
25
|
+
ping?: EventSchemaEntry<SV>;
|
|
26
|
+
pong?: EventSchemaEntry<SV>;
|
|
27
|
+
clientMessages: Record<string, EventSchemaEntry<SV>>;
|
|
28
|
+
serverMessages: Record<string, EventSchemaEntry<SV>>;
|
|
29
|
+
errors?: Record<string, EventSchemaEntry<SV>>;
|
|
30
|
+
closeReason?: Record<string, EventSchemaEntry<SV>>;
|
|
31
|
+
context?: StringOnlyObject<SV>;
|
|
32
|
+
userData?: StringOnlyObject<SV>;
|
|
33
|
+
};
|
|
34
|
+
type ServerEventSchema<SV extends AnySchemaValidator, ES extends EventSchema<SV>> = Omit<ES, 'serverMessages' | 'clientMessages'> & {
|
|
35
|
+
serverMessages: ES['clientMessages'];
|
|
36
|
+
clientMessages: ES['serverMessages'];
|
|
37
|
+
};
|
|
38
|
+
type ExtractSchemaFromEntry<SV extends AnySchemaValidator, Entry extends EventSchemaEntry<SV> | undefined> = Entry extends EventSchemaEntry<SV> ? Entry['shape'] : never;
|
|
39
|
+
type ExtractSchemaFromRecord<SV extends AnySchemaValidator, RecordType extends Record<string, EventSchemaEntry<SV>> | undefined> = RecordType extends Record<string, EventSchemaEntry<SV>> ? RecordType[keyof RecordType]['shape'] : never;
|
|
40
|
+
|
|
41
|
+
type AsyncApiDocument = AsyncAPIObject;
|
|
42
|
+
declare function generateAsyncApi<SV extends AnySchemaValidator>(eventSchemas: EventSchema<SV>, options?: {
|
|
43
|
+
title?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
id?: AsyncApiDocument['id'];
|
|
47
|
+
defaultContentType?: AsyncApiDocument['defaultContentType'];
|
|
48
|
+
}): AsyncApiDocument;
|
|
49
|
+
|
|
50
|
+
type SchemaRecord<SV extends AnySchemaValidator> = Record<string, EventSchemaEntry<SV>> | undefined;
|
|
51
|
+
declare function buildUnionSchema<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, record: SchemaRecord<SV>): IdiomaticSchema<SV> | undefined;
|
|
52
|
+
declare function parseSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, value: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
53
|
+
declare function encodeSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, value: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
54
|
+
declare function decodeSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, data: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
55
|
+
declare function normalizeEncodedValue(encoded: unknown, context: string, allowUndefined?: boolean): Buffer | undefined;
|
|
56
|
+
declare function createWebSocketSchemas<SV extends AnySchemaValidator, ES extends EventSchema<SV>>(schemaValidator: SchemaValidator, eventSchemas: ES): {
|
|
57
|
+
clientMessagesSchema: IdiomaticSchema<SV> | undefined;
|
|
58
|
+
serverMessagesSchema: IdiomaticSchema<SV> | undefined;
|
|
59
|
+
errorsSchema: IdiomaticSchema<SV> | undefined;
|
|
60
|
+
pingSchema: IdiomaticSchema<SV> | undefined;
|
|
61
|
+
pongSchema: IdiomaticSchema<SV> | undefined;
|
|
62
|
+
closeReasonSchema: IdiomaticSchema<SV> | undefined;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { type AsyncApiDocument, type AsyncApiEnrichment, type EventSchema, type EventSchemaEntry, type ExtractSchemaFromEntry, type ExtractSchemaFromRecord, type ServerEventSchema, buildUnionSchema, createWebSocketSchemas, decodeSchemaValue, encodeSchemaValue, generateAsyncApi, normalizeEncodedValue, parseSchemaValue };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AsyncAPIObject } from '@asyncapi/parser/esm/spec-types/v3';
|
|
2
|
+
import { AnySchemaValidator, IdiomaticSchema, SchemaValidator } from '@forklaunch/validator';
|
|
3
|
+
import { S as StringOnlyObject } from '../apiDefinition.types-BYizofKE.js';
|
|
4
|
+
import '@forklaunch/common';
|
|
5
|
+
import '@opentelemetry/api';
|
|
6
|
+
import 'jose';
|
|
7
|
+
import 'qs';
|
|
8
|
+
import 'stream';
|
|
9
|
+
import 'pino';
|
|
10
|
+
import '@forklaunch/fastmcp-fork';
|
|
11
|
+
import 'cors';
|
|
12
|
+
import 'http';
|
|
13
|
+
import '@scalar/express-api-reference';
|
|
14
|
+
|
|
15
|
+
type AsyncApiEnrichment = {
|
|
16
|
+
channel?: string;
|
|
17
|
+
channels?: string[];
|
|
18
|
+
operation?: string;
|
|
19
|
+
operations?: string[];
|
|
20
|
+
};
|
|
21
|
+
type EventSchemaEntry<SV extends AnySchemaValidator> = {
|
|
22
|
+
shape: IdiomaticSchema<SV>;
|
|
23
|
+
} & AsyncApiEnrichment;
|
|
24
|
+
type EventSchema<SV extends AnySchemaValidator> = {
|
|
25
|
+
ping?: EventSchemaEntry<SV>;
|
|
26
|
+
pong?: EventSchemaEntry<SV>;
|
|
27
|
+
clientMessages: Record<string, EventSchemaEntry<SV>>;
|
|
28
|
+
serverMessages: Record<string, EventSchemaEntry<SV>>;
|
|
29
|
+
errors?: Record<string, EventSchemaEntry<SV>>;
|
|
30
|
+
closeReason?: Record<string, EventSchemaEntry<SV>>;
|
|
31
|
+
context?: StringOnlyObject<SV>;
|
|
32
|
+
userData?: StringOnlyObject<SV>;
|
|
33
|
+
};
|
|
34
|
+
type ServerEventSchema<SV extends AnySchemaValidator, ES extends EventSchema<SV>> = Omit<ES, 'serverMessages' | 'clientMessages'> & {
|
|
35
|
+
serverMessages: ES['clientMessages'];
|
|
36
|
+
clientMessages: ES['serverMessages'];
|
|
37
|
+
};
|
|
38
|
+
type ExtractSchemaFromEntry<SV extends AnySchemaValidator, Entry extends EventSchemaEntry<SV> | undefined> = Entry extends EventSchemaEntry<SV> ? Entry['shape'] : never;
|
|
39
|
+
type ExtractSchemaFromRecord<SV extends AnySchemaValidator, RecordType extends Record<string, EventSchemaEntry<SV>> | undefined> = RecordType extends Record<string, EventSchemaEntry<SV>> ? RecordType[keyof RecordType]['shape'] : never;
|
|
40
|
+
|
|
41
|
+
type AsyncApiDocument = AsyncAPIObject;
|
|
42
|
+
declare function generateAsyncApi<SV extends AnySchemaValidator>(eventSchemas: EventSchema<SV>, options?: {
|
|
43
|
+
title?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
id?: AsyncApiDocument['id'];
|
|
47
|
+
defaultContentType?: AsyncApiDocument['defaultContentType'];
|
|
48
|
+
}): AsyncApiDocument;
|
|
49
|
+
|
|
50
|
+
type SchemaRecord<SV extends AnySchemaValidator> = Record<string, EventSchemaEntry<SV>> | undefined;
|
|
51
|
+
declare function buildUnionSchema<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, record: SchemaRecord<SV>): IdiomaticSchema<SV> | undefined;
|
|
52
|
+
declare function parseSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, value: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
53
|
+
declare function encodeSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, value: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
54
|
+
declare function decodeSchemaValue<SV extends AnySchemaValidator>(schemaValidator: SchemaValidator, data: unknown, schema: IdiomaticSchema<SV> | undefined, context: string): unknown;
|
|
55
|
+
declare function normalizeEncodedValue(encoded: unknown, context: string, allowUndefined?: boolean): Buffer | undefined;
|
|
56
|
+
declare function createWebSocketSchemas<SV extends AnySchemaValidator, ES extends EventSchema<SV>>(schemaValidator: SchemaValidator, eventSchemas: ES): {
|
|
57
|
+
clientMessagesSchema: IdiomaticSchema<SV> | undefined;
|
|
58
|
+
serverMessagesSchema: IdiomaticSchema<SV> | undefined;
|
|
59
|
+
errorsSchema: IdiomaticSchema<SV> | undefined;
|
|
60
|
+
pingSchema: IdiomaticSchema<SV> | undefined;
|
|
61
|
+
pongSchema: IdiomaticSchema<SV> | undefined;
|
|
62
|
+
closeReasonSchema: IdiomaticSchema<SV> | undefined;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { type AsyncApiDocument, type AsyncApiEnrichment, type EventSchema, type EventSchemaEntry, type ExtractSchemaFromEntry, type ExtractSchemaFromRecord, type ServerEventSchema, buildUnionSchema, createWebSocketSchemas, decodeSchemaValue, encodeSchemaValue, generateAsyncApi, normalizeEncodedValue, parseSchemaValue };
|
package/lib/ws/index.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ws/index.ts
|
|
21
|
+
var ws_exports = {};
|
|
22
|
+
__export(ws_exports, {
|
|
23
|
+
buildUnionSchema: () => buildUnionSchema,
|
|
24
|
+
createWebSocketSchemas: () => createWebSocketSchemas,
|
|
25
|
+
decodeSchemaValue: () => decodeSchemaValue,
|
|
26
|
+
encodeSchemaValue: () => encodeSchemaValue,
|
|
27
|
+
generateAsyncApi: () => generateAsyncApi,
|
|
28
|
+
normalizeEncodedValue: () => normalizeEncodedValue,
|
|
29
|
+
parseSchemaValue: () => parseSchemaValue
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(ws_exports);
|
|
32
|
+
|
|
33
|
+
// src/ws/asyncApiV3Generator/asyncApiV3Generator.ts
|
|
34
|
+
function generateAsyncApi(eventSchemas, options) {
|
|
35
|
+
const channels = {};
|
|
36
|
+
const appendMessageToChannel = (collection) => {
|
|
37
|
+
if (!collection) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
Object.entries(collection).forEach(([messageKey, candidate]) => {
|
|
41
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate) || !("shape" in candidate)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const entry = candidate;
|
|
45
|
+
const schema = entry.shape;
|
|
46
|
+
const channelNames = entry.channel ? [entry.channel] : entry.channels && entry.channels.length > 0 ? entry.channels : [messageKey];
|
|
47
|
+
const messageName = entry.operation ?? entry.operations?.[0] ?? messageKey;
|
|
48
|
+
channelNames.forEach((channelName) => {
|
|
49
|
+
const existingChannel = channels[channelName];
|
|
50
|
+
if (!existingChannel || "$ref" in existingChannel) {
|
|
51
|
+
channels[channelName] = {
|
|
52
|
+
address: channelName,
|
|
53
|
+
messages: {}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const channelEntry = channels[channelName];
|
|
57
|
+
channelEntry.messages = channelEntry.messages ?? {};
|
|
58
|
+
const messages = channelEntry.messages;
|
|
59
|
+
const message = {
|
|
60
|
+
name: messageName,
|
|
61
|
+
payload: schema
|
|
62
|
+
};
|
|
63
|
+
messages[messageName] = message;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
appendMessageToChannel(eventSchemas.clientMessages);
|
|
68
|
+
appendMessageToChannel(eventSchemas.serverMessages);
|
|
69
|
+
appendMessageToChannel(eventSchemas.errors);
|
|
70
|
+
const operations = {};
|
|
71
|
+
const addOperationsForCollection = (collection, action) => {
|
|
72
|
+
if (!collection) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
Object.entries(collection).forEach(([messageKey, candidate]) => {
|
|
76
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate) || !("shape" in candidate)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const entry = candidate;
|
|
80
|
+
const channelNames = entry.channel ? [entry.channel] : entry.channels && entry.channels.length > 0 ? entry.channels : [messageKey];
|
|
81
|
+
const messageName = entry.operation ?? entry.operations?.[0] ?? messageKey;
|
|
82
|
+
channelNames.forEach((channelName) => {
|
|
83
|
+
const operationKey = `${action}-${channelName}-${messageName}`;
|
|
84
|
+
const operation = {
|
|
85
|
+
action,
|
|
86
|
+
channel: {
|
|
87
|
+
$ref: `#/channels/${channelName}`
|
|
88
|
+
},
|
|
89
|
+
messages: [
|
|
90
|
+
{
|
|
91
|
+
$ref: `#/channels/${channelName}/messages/${messageName}`
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
};
|
|
95
|
+
operations[operationKey] = operation;
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
addOperationsForCollection(eventSchemas.clientMessages, "receive");
|
|
100
|
+
addOperationsForCollection(eventSchemas.serverMessages, "send");
|
|
101
|
+
const { title, version, description } = options ?? {};
|
|
102
|
+
const asyncApiDocument = {
|
|
103
|
+
asyncapi: "3.0.0",
|
|
104
|
+
info: {
|
|
105
|
+
title: title || process.env.API_TITLE || "Forklaunch WebSocket API",
|
|
106
|
+
version: version || "1.0.0",
|
|
107
|
+
...description ? { description } : {}
|
|
108
|
+
},
|
|
109
|
+
...options?.id ? { id: options.id } : {},
|
|
110
|
+
...options?.defaultContentType ? { defaultContentType: options.defaultContentType } : {},
|
|
111
|
+
...Object.keys(channels).length > 0 ? { channels } : {},
|
|
112
|
+
...Object.keys(operations).length > 0 ? { operations } : {}
|
|
113
|
+
};
|
|
114
|
+
return asyncApiDocument;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/ws/webSocketLike.ts
|
|
118
|
+
var import_validator = require("@forklaunch/validator");
|
|
119
|
+
function buildUnionSchema(schemaValidator, record) {
|
|
120
|
+
if (!record) {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
const schemas = Object.values(record).filter((entry) => Boolean(entry)).map((entry) => entry.shape);
|
|
124
|
+
if (schemas.length === 0) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
if (schemas.length === 1) {
|
|
128
|
+
return schemas[0];
|
|
129
|
+
}
|
|
130
|
+
return schemaValidator.union(schemas);
|
|
131
|
+
}
|
|
132
|
+
function parseSchemaValue(schemaValidator, value, schema, context) {
|
|
133
|
+
if (!schema) {
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
const result = schemaValidator.parse(schema, value);
|
|
137
|
+
if (result.ok) {
|
|
138
|
+
return result.value;
|
|
139
|
+
}
|
|
140
|
+
const errors = "errors" in result ? result.errors || [] : [];
|
|
141
|
+
throw new Error((0, import_validator.prettyPrintParseErrors)(errors, context));
|
|
142
|
+
}
|
|
143
|
+
function encodeSchemaValue(schemaValidator, value, schema, context) {
|
|
144
|
+
const validated = parseSchemaValue(schemaValidator, value, schema, context);
|
|
145
|
+
if (Buffer.isBuffer(validated) || validated instanceof ArrayBuffer || ArrayBuffer.isView(validated)) {
|
|
146
|
+
if (Buffer.isBuffer(validated)) {
|
|
147
|
+
return validated;
|
|
148
|
+
}
|
|
149
|
+
if (validated instanceof ArrayBuffer) {
|
|
150
|
+
return Buffer.from(validated);
|
|
151
|
+
}
|
|
152
|
+
const view = validated;
|
|
153
|
+
const typed = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
154
|
+
return Buffer.from(typed);
|
|
155
|
+
}
|
|
156
|
+
if (typeof validated === "string") {
|
|
157
|
+
return Buffer.from(validated, "utf-8");
|
|
158
|
+
}
|
|
159
|
+
if (validated === null || validated === void 0) {
|
|
160
|
+
return validated;
|
|
161
|
+
}
|
|
162
|
+
return Buffer.from(JSON.stringify(validated), "utf-8");
|
|
163
|
+
}
|
|
164
|
+
function decodeSchemaValue(schemaValidator, data, schema, context) {
|
|
165
|
+
let decoded;
|
|
166
|
+
let parsed;
|
|
167
|
+
if (Buffer.isBuffer(data)) {
|
|
168
|
+
decoded = data.toString("utf-8");
|
|
169
|
+
} else if (data instanceof ArrayBuffer) {
|
|
170
|
+
decoded = Buffer.from(data).toString("utf-8");
|
|
171
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
172
|
+
const view = data;
|
|
173
|
+
decoded = Buffer.from(
|
|
174
|
+
view.buffer,
|
|
175
|
+
view.byteOffset,
|
|
176
|
+
view.byteLength
|
|
177
|
+
).toString("utf-8");
|
|
178
|
+
} else if (typeof data === "string") {
|
|
179
|
+
decoded = data;
|
|
180
|
+
} else {
|
|
181
|
+
parsed = data;
|
|
182
|
+
return parseSchemaValue(schemaValidator, parsed, schema, context);
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
parsed = JSON.parse(decoded);
|
|
186
|
+
} catch {
|
|
187
|
+
parsed = decoded;
|
|
188
|
+
}
|
|
189
|
+
return parseSchemaValue(schemaValidator, parsed, schema, context);
|
|
190
|
+
}
|
|
191
|
+
function normalizeEncodedValue(encoded, context, allowUndefined = false) {
|
|
192
|
+
if (encoded === null || encoded === void 0) {
|
|
193
|
+
if (allowUndefined) {
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
throw new Error(`Invalid ${context}`);
|
|
197
|
+
}
|
|
198
|
+
if (Buffer.isBuffer(encoded)) {
|
|
199
|
+
return encoded;
|
|
200
|
+
}
|
|
201
|
+
if (encoded instanceof ArrayBuffer) {
|
|
202
|
+
return Buffer.from(encoded);
|
|
203
|
+
}
|
|
204
|
+
if (ArrayBuffer.isView(encoded)) {
|
|
205
|
+
const view = encoded;
|
|
206
|
+
return Buffer.from(view.buffer, view.byteOffset, view.byteLength);
|
|
207
|
+
}
|
|
208
|
+
if (typeof encoded === "string") {
|
|
209
|
+
return Buffer.from(encoded, "utf-8");
|
|
210
|
+
}
|
|
211
|
+
if (typeof encoded === "number" || typeof encoded === "boolean" || typeof encoded === "bigint") {
|
|
212
|
+
return Buffer.from(JSON.stringify(encoded), "utf-8");
|
|
213
|
+
}
|
|
214
|
+
if (typeof encoded === "object") {
|
|
215
|
+
return Buffer.from(JSON.stringify(encoded), "utf-8");
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`Unsupported payload type for ${context}`);
|
|
218
|
+
}
|
|
219
|
+
function createWebSocketSchemas(schemaValidator, eventSchemas) {
|
|
220
|
+
const clientMessagesSchema = buildUnionSchema(
|
|
221
|
+
schemaValidator,
|
|
222
|
+
eventSchemas.clientMessages
|
|
223
|
+
);
|
|
224
|
+
const serverMessagesSchema = buildUnionSchema(
|
|
225
|
+
schemaValidator,
|
|
226
|
+
eventSchemas.serverMessages
|
|
227
|
+
);
|
|
228
|
+
const errorsSchema = buildUnionSchema(
|
|
229
|
+
schemaValidator,
|
|
230
|
+
eventSchemas.errors
|
|
231
|
+
);
|
|
232
|
+
const pingSchema = eventSchemas.ping?.shape;
|
|
233
|
+
const pongSchema = eventSchemas.pong?.shape;
|
|
234
|
+
const closeReasonSchema = eventSchemas.closeReason ? buildUnionSchema(schemaValidator, eventSchemas.closeReason) : void 0;
|
|
235
|
+
return {
|
|
236
|
+
clientMessagesSchema,
|
|
237
|
+
serverMessagesSchema,
|
|
238
|
+
errorsSchema,
|
|
239
|
+
pingSchema,
|
|
240
|
+
pongSchema,
|
|
241
|
+
closeReasonSchema
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
buildUnionSchema,
|
|
247
|
+
createWebSocketSchemas,
|
|
248
|
+
decodeSchemaValue,
|
|
249
|
+
encodeSchemaValue,
|
|
250
|
+
generateAsyncApi,
|
|
251
|
+
normalizeEncodedValue,
|
|
252
|
+
parseSchemaValue
|
|
253
|
+
});
|
|
254
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ws/index.ts","../../src/ws/asyncApiV3Generator/asyncApiV3Generator.ts","../../src/ws/webSocketLike.ts"],"sourcesContent":["export * from './asyncApiV3Generator/asyncApiV3Generator';\nexport * from './types/eventSchema.types';\nexport * from './webSocketLike';\n","import type {\n AsyncAPIObject,\n ChannelObject,\n ChannelsObject,\n MessageObject,\n MessagesObject,\n OperationObject,\n OperationsObject,\n ReferenceObject\n} from '@asyncapi/parser/esm/spec-types/v3';\n\nimport type { AnySchemaValidator } from '@forklaunch/validator';\n\nimport { EventSchema, EventSchemaEntry } from '../types/eventSchema.types';\n\nexport type AsyncApiDocument = AsyncAPIObject;\n\nexport function generateAsyncApi<SV extends AnySchemaValidator>(\n eventSchemas: EventSchema<SV>,\n options?: {\n title?: string;\n version?: string;\n description?: string;\n id?: AsyncApiDocument['id'];\n defaultContentType?: AsyncApiDocument['defaultContentType'];\n }\n): AsyncApiDocument {\n const channels: ChannelsObject = {};\n\n const appendMessageToChannel = (\n collection?: Record<string, EventSchemaEntry<SV>>\n ) => {\n if (!collection) {\n return;\n }\n\n Object.entries(collection).forEach(([messageKey, candidate]) => {\n if (\n !candidate ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate) ||\n !('shape' in candidate)\n ) {\n return;\n }\n\n const entry = candidate as EventSchemaEntry<SV>;\n const schema = entry.shape;\n\n // Determine which channels to use\n const channelNames: string[] = entry.channel\n ? [entry.channel]\n : entry.channels && entry.channels.length > 0\n ? entry.channels\n : [messageKey];\n\n const messageName =\n entry.operation ?? entry.operations?.[0] ?? messageKey;\n\n // Add message to all specified channels\n channelNames.forEach((channelName) => {\n const existingChannel = channels[channelName];\n if (!existingChannel || '$ref' in existingChannel) {\n channels[channelName] = {\n address: channelName,\n messages: {}\n };\n }\n\n const channelEntry = channels[channelName] as ChannelObject;\n channelEntry.messages = (channelEntry.messages ?? {}) as MessagesObject;\n const messages = channelEntry.messages as MessagesObject;\n\n const message: MessageObject = {\n name: messageName,\n payload: schema\n };\n\n messages[messageName] = message;\n });\n });\n };\n\n appendMessageToChannel(eventSchemas.clientMessages);\n appendMessageToChannel(eventSchemas.serverMessages);\n appendMessageToChannel(eventSchemas.errors);\n\n const operations: OperationsObject = {};\n\n const addOperationsForCollection = (\n collection: Record<string, EventSchemaEntry<SV>> | undefined,\n action: 'send' | 'receive'\n ) => {\n if (!collection) {\n return;\n }\n\n Object.entries(collection).forEach(([messageKey, candidate]) => {\n if (\n !candidate ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate) ||\n !('shape' in candidate)\n ) {\n return;\n }\n\n const entry = candidate as EventSchemaEntry<SV>;\n\n // Determine which channels to use\n const channelNames: string[] = entry.channel\n ? [entry.channel]\n : entry.channels && entry.channels.length > 0\n ? entry.channels\n : [messageKey];\n\n const messageName =\n entry.operation ?? entry.operations?.[0] ?? messageKey;\n\n // Create operations for all specified channels\n channelNames.forEach((channelName) => {\n const operationKey = `${action}-${channelName}-${messageName}`;\n\n const operation: OperationObject = {\n action,\n channel: {\n $ref: `#/channels/${channelName}`\n } as ReferenceObject,\n messages: [\n {\n $ref: `#/channels/${channelName}/messages/${messageName}`\n } as ReferenceObject\n ]\n };\n\n operations[operationKey] = operation;\n });\n });\n };\n\n addOperationsForCollection(eventSchemas.clientMessages, 'receive');\n addOperationsForCollection(eventSchemas.serverMessages, 'send');\n\n const { title, version, description } = options ?? {};\n\n const asyncApiDocument: AsyncApiDocument = {\n asyncapi: '3.0.0',\n info: {\n title: title || process.env.API_TITLE || 'Forklaunch WebSocket API',\n version: version || '1.0.0',\n ...(description ? { description } : {})\n },\n ...(options?.id ? { id: options.id } : {}),\n ...(options?.defaultContentType\n ? { defaultContentType: options.defaultContentType }\n : {}),\n ...(Object.keys(channels).length > 0 ? { channels } : {}),\n ...(Object.keys(operations).length > 0 ? { operations } : {})\n };\n\n return asyncApiDocument;\n}\n","import {\n AnySchemaValidator,\n IdiomaticSchema,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\n\nimport { EventSchema, EventSchemaEntry } from './types/eventSchema.types';\n\ntype SchemaRecord<SV extends AnySchemaValidator> =\n | Record<string, EventSchemaEntry<SV>>\n | undefined;\n\nexport function buildUnionSchema<SV extends AnySchemaValidator>(\n schemaValidator: SchemaValidator,\n record: SchemaRecord<SV>\n): IdiomaticSchema<SV> | undefined {\n if (!record) {\n return undefined;\n }\n\n const schemas = Object.values(record)\n .filter((entry): entry is EventSchemaEntry<SV> => Boolean(entry))\n .map((entry) => entry.shape);\n\n if (schemas.length === 0) {\n return undefined;\n }\n\n if (schemas.length === 1) {\n return schemas[0];\n }\n\n return schemaValidator.union(schemas) as IdiomaticSchema<SV>;\n}\n\nexport function parseSchemaValue<SV extends AnySchemaValidator>(\n schemaValidator: SchemaValidator,\n value: unknown,\n schema: IdiomaticSchema<SV> | undefined,\n context: string\n): unknown {\n if (!schema) {\n return value;\n }\n\n const result = schemaValidator.parse(schema, value);\n if (result.ok) {\n return result.value;\n }\n\n const errors = 'errors' in result ? result.errors || [] : [];\n throw new Error(prettyPrintParseErrors(errors, context));\n}\n\nexport function encodeSchemaValue<SV extends AnySchemaValidator>(\n schemaValidator: SchemaValidator,\n value: unknown,\n schema: IdiomaticSchema<SV> | undefined,\n context: string\n): unknown {\n const validated = parseSchemaValue(schemaValidator, value, schema, context);\n\n if (\n Buffer.isBuffer(validated) ||\n validated instanceof ArrayBuffer ||\n ArrayBuffer.isView(validated)\n ) {\n if (Buffer.isBuffer(validated)) {\n return validated;\n }\n\n if (validated instanceof ArrayBuffer) {\n return Buffer.from(validated);\n }\n\n const view = validated as {\n buffer: ArrayBuffer;\n byteOffset: number;\n byteLength: number;\n };\n const typed = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);\n return Buffer.from(typed);\n }\n\n if (typeof validated === 'string') {\n return Buffer.from(validated, 'utf-8');\n }\n\n if (validated === null || validated === undefined) {\n return validated;\n }\n\n return Buffer.from(JSON.stringify(validated), 'utf-8');\n}\n\nexport function decodeSchemaValue<SV extends AnySchemaValidator>(\n schemaValidator: SchemaValidator,\n data: unknown,\n schema: IdiomaticSchema<SV> | undefined,\n context: string\n): unknown {\n let decoded: string;\n let parsed: unknown;\n\n if (Buffer.isBuffer(data)) {\n decoded = data.toString('utf-8');\n } else if (data instanceof ArrayBuffer) {\n decoded = Buffer.from(data).toString('utf-8');\n } else if (ArrayBuffer.isView(data)) {\n const view = data as {\n buffer: ArrayBuffer;\n byteOffset: number;\n byteLength: number;\n };\n decoded = Buffer.from(\n view.buffer,\n view.byteOffset,\n view.byteLength\n ).toString('utf-8');\n } else if (typeof data === 'string') {\n decoded = data;\n } else {\n parsed = data;\n return parseSchemaValue(schemaValidator, parsed, schema, context);\n }\n\n try {\n parsed = JSON.parse(decoded);\n } catch {\n parsed = decoded;\n }\n\n return parseSchemaValue(schemaValidator, parsed, schema, context);\n}\n\nexport function normalizeEncodedValue(\n encoded: unknown,\n context: string,\n allowUndefined = false\n): Buffer | undefined {\n if (encoded === null || encoded === undefined) {\n if (allowUndefined) {\n return undefined;\n }\n throw new Error(`Invalid ${context}`);\n }\n\n if (Buffer.isBuffer(encoded)) {\n return encoded;\n }\n\n if (encoded instanceof ArrayBuffer) {\n return Buffer.from(encoded);\n }\n\n if (ArrayBuffer.isView(encoded)) {\n const view = encoded as ArrayBufferView;\n return Buffer.from(view.buffer, view.byteOffset, view.byteLength);\n }\n\n if (typeof encoded === 'string') {\n return Buffer.from(encoded, 'utf-8');\n }\n\n if (\n typeof encoded === 'number' ||\n typeof encoded === 'boolean' ||\n typeof encoded === 'bigint'\n ) {\n return Buffer.from(JSON.stringify(encoded), 'utf-8');\n }\n\n if (typeof encoded === 'object') {\n return Buffer.from(JSON.stringify(encoded), 'utf-8');\n }\n\n throw new Error(`Unsupported payload type for ${context}`);\n}\n\nexport function createWebSocketSchemas<\n SV extends AnySchemaValidator,\n ES extends EventSchema<SV>\n>(schemaValidator: SchemaValidator, eventSchemas: ES) {\n const clientMessagesSchema = buildUnionSchema<SV>(\n schemaValidator,\n eventSchemas.clientMessages\n );\n const serverMessagesSchema = buildUnionSchema<SV>(\n schemaValidator,\n eventSchemas.serverMessages\n );\n const errorsSchema = buildUnionSchema<SV>(\n schemaValidator,\n eventSchemas.errors\n );\n const pingSchema = eventSchemas.ping?.shape;\n const pongSchema = eventSchemas.pong?.shape;\n const closeReasonSchema = eventSchemas.closeReason\n ? buildUnionSchema<SV>(schemaValidator, eventSchemas.closeReason)\n : undefined;\n\n return {\n clientMessagesSchema,\n serverMessagesSchema,\n errorsSchema,\n pingSchema,\n pongSchema,\n closeReasonSchema\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiBO,SAAS,iBACd,cACA,SAOkB;AAClB,QAAM,WAA2B,CAAC;AAElC,QAAM,yBAAyB,CAC7B,eACG;AACH,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,WAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,YAAY,SAAS,MAAM;AAC9D,UACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,KACvB,EAAE,WAAW,YACb;AACA;AAAA,MACF;AAEA,YAAM,QAAQ;AACd,YAAM,SAAS,MAAM;AAGrB,YAAM,eAAyB,MAAM,UACjC,CAAC,MAAM,OAAO,IACd,MAAM,YAAY,MAAM,SAAS,SAAS,IACxC,MAAM,WACN,CAAC,UAAU;AAEjB,YAAM,cACJ,MAAM,aAAa,MAAM,aAAa,CAAC,KAAK;AAG9C,mBAAa,QAAQ,CAAC,gBAAgB;AACpC,cAAM,kBAAkB,SAAS,WAAW;AAC5C,YAAI,CAAC,mBAAmB,UAAU,iBAAiB;AACjD,mBAAS,WAAW,IAAI;AAAA,YACtB,SAAS;AAAA,YACT,UAAU,CAAC;AAAA,UACb;AAAA,QACF;AAEA,cAAM,eAAe,SAAS,WAAW;AACzC,qBAAa,WAAY,aAAa,YAAY,CAAC;AACnD,cAAM,WAAW,aAAa;AAE9B,cAAM,UAAyB;AAAA,UAC7B,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAEA,iBAAS,WAAW,IAAI;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,yBAAuB,aAAa,cAAc;AAClD,yBAAuB,aAAa,cAAc;AAClD,yBAAuB,aAAa,MAAM;AAE1C,QAAM,aAA+B,CAAC;AAEtC,QAAM,6BAA6B,CACjC,YACA,WACG;AACH,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AAEA,WAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,YAAY,SAAS,MAAM;AAC9D,UACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,KACvB,EAAE,WAAW,YACb;AACA;AAAA,MACF;AAEA,YAAM,QAAQ;AAGd,YAAM,eAAyB,MAAM,UACjC,CAAC,MAAM,OAAO,IACd,MAAM,YAAY,MAAM,SAAS,SAAS,IACxC,MAAM,WACN,CAAC,UAAU;AAEjB,YAAM,cACJ,MAAM,aAAa,MAAM,aAAa,CAAC,KAAK;AAG9C,mBAAa,QAAQ,CAAC,gBAAgB;AACpC,cAAM,eAAe,GAAG,MAAM,IAAI,WAAW,IAAI,WAAW;AAE5D,cAAM,YAA6B;AAAA,UACjC;AAAA,UACA,SAAS;AAAA,YACP,MAAM,cAAc,WAAW;AAAA,UACjC;AAAA,UACA,UAAU;AAAA,YACR;AAAA,cACE,MAAM,cAAc,WAAW,aAAa,WAAW;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AAEA,mBAAW,YAAY,IAAI;AAAA,MAC7B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,6BAA2B,aAAa,gBAAgB,SAAS;AACjE,6BAA2B,aAAa,gBAAgB,MAAM;AAE9D,QAAM,EAAE,OAAO,SAAS,YAAY,IAAI,WAAW,CAAC;AAEpD,QAAM,mBAAqC;AAAA,IACzC,UAAU;AAAA,IACV,MAAM;AAAA,MACJ,OAAO,SAAS,QAAQ,IAAI,aAAa;AAAA,MACzC,SAAS,WAAW;AAAA,MACpB,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACvC;AAAA,IACA,GAAI,SAAS,KAAK,EAAE,IAAI,QAAQ,GAAG,IAAI,CAAC;AAAA,IACxC,GAAI,SAAS,qBACT,EAAE,oBAAoB,QAAQ,mBAAmB,IACjD,CAAC;AAAA,IACL,GAAI,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,IACvD,GAAI,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,EAAE,WAAW,IAAI,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;;;ACjKA,uBAKO;AAQA,SAAS,iBACd,iBACA,QACiC;AACjC,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,OAAO,MAAM,EACjC,OAAO,CAAC,UAAyC,QAAQ,KAAK,CAAC,EAC/D,IAAI,CAAC,UAAU,MAAM,KAAK;AAE7B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,QAAQ,CAAC;AAAA,EAClB;AAEA,SAAO,gBAAgB,MAAM,OAAO;AACtC;AAEO,SAAS,iBACd,iBACA,OACA,QACA,SACS;AACT,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,gBAAgB,MAAM,QAAQ,KAAK;AAClD,MAAI,OAAO,IAAI;AACb,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,SAAS,YAAY,SAAS,OAAO,UAAU,CAAC,IAAI,CAAC;AAC3D,QAAM,IAAI,UAAM,yCAAuB,QAAQ,OAAO,CAAC;AACzD;AAEO,SAAS,kBACd,iBACA,OACA,QACA,SACS;AACT,QAAM,YAAY,iBAAiB,iBAAiB,OAAO,QAAQ,OAAO;AAE1E,MACE,OAAO,SAAS,SAAS,KACzB,qBAAqB,eACrB,YAAY,OAAO,SAAS,GAC5B;AACA,QAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,qBAAqB,aAAa;AACpC,aAAO,OAAO,KAAK,SAAS;AAAA,IAC9B;AAEA,UAAM,OAAO;AAKb,UAAM,QAAQ,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU;AAC1E,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B;AAEA,MAAI,OAAO,cAAc,UAAU;AACjC,WAAO,OAAO,KAAK,WAAW,OAAO;AAAA,EACvC;AAEA,MAAI,cAAc,QAAQ,cAAc,QAAW;AACjD,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,KAAK,KAAK,UAAU,SAAS,GAAG,OAAO;AACvD;AAEO,SAAS,kBACd,iBACA,MACA,QACA,SACS;AACT,MAAI;AACJ,MAAI;AAEJ,MAAI,OAAO,SAAS,IAAI,GAAG;AACzB,cAAU,KAAK,SAAS,OAAO;AAAA,EACjC,WAAW,gBAAgB,aAAa;AACtC,cAAU,OAAO,KAAK,IAAI,EAAE,SAAS,OAAO;AAAA,EAC9C,WAAW,YAAY,OAAO,IAAI,GAAG;AACnC,UAAM,OAAO;AAKb,cAAU,OAAO;AAAA,MACf,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP,EAAE,SAAS,OAAO;AAAA,EACpB,WAAW,OAAO,SAAS,UAAU;AACnC,cAAU;AAAA,EACZ,OAAO;AACL,aAAS;AACT,WAAO,iBAAiB,iBAAiB,QAAQ,QAAQ,OAAO;AAAA,EAClE;AAEA,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,aAAS;AAAA,EACX;AAEA,SAAO,iBAAiB,iBAAiB,QAAQ,QAAQ,OAAO;AAClE;AAEO,SAAS,sBACd,SACA,SACA,iBAAiB,OACG;AACpB,MAAI,YAAY,QAAQ,YAAY,QAAW;AAC7C,QAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AACA,UAAM,IAAI,MAAM,WAAW,OAAO,EAAE;AAAA,EACtC;AAEA,MAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,mBAAmB,aAAa;AAClC,WAAO,OAAO,KAAK,OAAO;AAAA,EAC5B;AAEA,MAAI,YAAY,OAAO,OAAO,GAAG;AAC/B,UAAM,OAAO;AACb,WAAO,OAAO,KAAK,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU;AAAA,EAClE;AAEA,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,OAAO,KAAK,SAAS,OAAO;AAAA,EACrC;AAEA,MACE,OAAO,YAAY,YACnB,OAAO,YAAY,aACnB,OAAO,YAAY,UACnB;AACA,WAAO,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,OAAO;AAAA,EACrD;AAEA,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,OAAO;AAAA,EACrD;AAEA,QAAM,IAAI,MAAM,gCAAgC,OAAO,EAAE;AAC3D;AAEO,SAAS,uBAGd,iBAAkC,cAAkB;AACpD,QAAM,uBAAuB;AAAA,IAC3B;AAAA,IACA,aAAa;AAAA,EACf;AACA,QAAM,uBAAuB;AAAA,IAC3B;AAAA,IACA,aAAa;AAAA,EACf;AACA,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,aAAa;AAAA,EACf;AACA,QAAM,aAAa,aAAa,MAAM;AACtC,QAAM,aAAa,aAAa,MAAM;AACtC,QAAM,oBAAoB,aAAa,cACnC,iBAAqB,iBAAiB,aAAa,WAAW,IAC9D;AAEJ,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
package/lib/ws/index.mjs
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// src/ws/asyncApiV3Generator/asyncApiV3Generator.ts
|
|
2
|
+
function generateAsyncApi(eventSchemas, options) {
|
|
3
|
+
const channels = {};
|
|
4
|
+
const appendMessageToChannel = (collection) => {
|
|
5
|
+
if (!collection) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
Object.entries(collection).forEach(([messageKey, candidate]) => {
|
|
9
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate) || !("shape" in candidate)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const entry = candidate;
|
|
13
|
+
const schema = entry.shape;
|
|
14
|
+
const channelNames = entry.channel ? [entry.channel] : entry.channels && entry.channels.length > 0 ? entry.channels : [messageKey];
|
|
15
|
+
const messageName = entry.operation ?? entry.operations?.[0] ?? messageKey;
|
|
16
|
+
channelNames.forEach((channelName) => {
|
|
17
|
+
const existingChannel = channels[channelName];
|
|
18
|
+
if (!existingChannel || "$ref" in existingChannel) {
|
|
19
|
+
channels[channelName] = {
|
|
20
|
+
address: channelName,
|
|
21
|
+
messages: {}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const channelEntry = channels[channelName];
|
|
25
|
+
channelEntry.messages = channelEntry.messages ?? {};
|
|
26
|
+
const messages = channelEntry.messages;
|
|
27
|
+
const message = {
|
|
28
|
+
name: messageName,
|
|
29
|
+
payload: schema
|
|
30
|
+
};
|
|
31
|
+
messages[messageName] = message;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
appendMessageToChannel(eventSchemas.clientMessages);
|
|
36
|
+
appendMessageToChannel(eventSchemas.serverMessages);
|
|
37
|
+
appendMessageToChannel(eventSchemas.errors);
|
|
38
|
+
const operations = {};
|
|
39
|
+
const addOperationsForCollection = (collection, action) => {
|
|
40
|
+
if (!collection) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
Object.entries(collection).forEach(([messageKey, candidate]) => {
|
|
44
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate) || !("shape" in candidate)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const entry = candidate;
|
|
48
|
+
const channelNames = entry.channel ? [entry.channel] : entry.channels && entry.channels.length > 0 ? entry.channels : [messageKey];
|
|
49
|
+
const messageName = entry.operation ?? entry.operations?.[0] ?? messageKey;
|
|
50
|
+
channelNames.forEach((channelName) => {
|
|
51
|
+
const operationKey = `${action}-${channelName}-${messageName}`;
|
|
52
|
+
const operation = {
|
|
53
|
+
action,
|
|
54
|
+
channel: {
|
|
55
|
+
$ref: `#/channels/${channelName}`
|
|
56
|
+
},
|
|
57
|
+
messages: [
|
|
58
|
+
{
|
|
59
|
+
$ref: `#/channels/${channelName}/messages/${messageName}`
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
operations[operationKey] = operation;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
addOperationsForCollection(eventSchemas.clientMessages, "receive");
|
|
68
|
+
addOperationsForCollection(eventSchemas.serverMessages, "send");
|
|
69
|
+
const { title, version, description } = options ?? {};
|
|
70
|
+
const asyncApiDocument = {
|
|
71
|
+
asyncapi: "3.0.0",
|
|
72
|
+
info: {
|
|
73
|
+
title: title || process.env.API_TITLE || "Forklaunch WebSocket API",
|
|
74
|
+
version: version || "1.0.0",
|
|
75
|
+
...description ? { description } : {}
|
|
76
|
+
},
|
|
77
|
+
...options?.id ? { id: options.id } : {},
|
|
78
|
+
...options?.defaultContentType ? { defaultContentType: options.defaultContentType } : {},
|
|
79
|
+
...Object.keys(channels).length > 0 ? { channels } : {},
|
|
80
|
+
...Object.keys(operations).length > 0 ? { operations } : {}
|
|
81
|
+
};
|
|
82
|
+
return asyncApiDocument;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/ws/webSocketLike.ts
|
|
86
|
+
import {
|
|
87
|
+
prettyPrintParseErrors
|
|
88
|
+
} from "@forklaunch/validator";
|
|
89
|
+
function buildUnionSchema(schemaValidator, record) {
|
|
90
|
+
if (!record) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
const schemas = Object.values(record).filter((entry) => Boolean(entry)).map((entry) => entry.shape);
|
|
94
|
+
if (schemas.length === 0) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
if (schemas.length === 1) {
|
|
98
|
+
return schemas[0];
|
|
99
|
+
}
|
|
100
|
+
return schemaValidator.union(schemas);
|
|
101
|
+
}
|
|
102
|
+
function parseSchemaValue(schemaValidator, value, schema, context) {
|
|
103
|
+
if (!schema) {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
const result = schemaValidator.parse(schema, value);
|
|
107
|
+
if (result.ok) {
|
|
108
|
+
return result.value;
|
|
109
|
+
}
|
|
110
|
+
const errors = "errors" in result ? result.errors || [] : [];
|
|
111
|
+
throw new Error(prettyPrintParseErrors(errors, context));
|
|
112
|
+
}
|
|
113
|
+
function encodeSchemaValue(schemaValidator, value, schema, context) {
|
|
114
|
+
const validated = parseSchemaValue(schemaValidator, value, schema, context);
|
|
115
|
+
if (Buffer.isBuffer(validated) || validated instanceof ArrayBuffer || ArrayBuffer.isView(validated)) {
|
|
116
|
+
if (Buffer.isBuffer(validated)) {
|
|
117
|
+
return validated;
|
|
118
|
+
}
|
|
119
|
+
if (validated instanceof ArrayBuffer) {
|
|
120
|
+
return Buffer.from(validated);
|
|
121
|
+
}
|
|
122
|
+
const view = validated;
|
|
123
|
+
const typed = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
124
|
+
return Buffer.from(typed);
|
|
125
|
+
}
|
|
126
|
+
if (typeof validated === "string") {
|
|
127
|
+
return Buffer.from(validated, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
if (validated === null || validated === void 0) {
|
|
130
|
+
return validated;
|
|
131
|
+
}
|
|
132
|
+
return Buffer.from(JSON.stringify(validated), "utf-8");
|
|
133
|
+
}
|
|
134
|
+
function decodeSchemaValue(schemaValidator, data, schema, context) {
|
|
135
|
+
let decoded;
|
|
136
|
+
let parsed;
|
|
137
|
+
if (Buffer.isBuffer(data)) {
|
|
138
|
+
decoded = data.toString("utf-8");
|
|
139
|
+
} else if (data instanceof ArrayBuffer) {
|
|
140
|
+
decoded = Buffer.from(data).toString("utf-8");
|
|
141
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
142
|
+
const view = data;
|
|
143
|
+
decoded = Buffer.from(
|
|
144
|
+
view.buffer,
|
|
145
|
+
view.byteOffset,
|
|
146
|
+
view.byteLength
|
|
147
|
+
).toString("utf-8");
|
|
148
|
+
} else if (typeof data === "string") {
|
|
149
|
+
decoded = data;
|
|
150
|
+
} else {
|
|
151
|
+
parsed = data;
|
|
152
|
+
return parseSchemaValue(schemaValidator, parsed, schema, context);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
parsed = JSON.parse(decoded);
|
|
156
|
+
} catch {
|
|
157
|
+
parsed = decoded;
|
|
158
|
+
}
|
|
159
|
+
return parseSchemaValue(schemaValidator, parsed, schema, context);
|
|
160
|
+
}
|
|
161
|
+
function normalizeEncodedValue(encoded, context, allowUndefined = false) {
|
|
162
|
+
if (encoded === null || encoded === void 0) {
|
|
163
|
+
if (allowUndefined) {
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`Invalid ${context}`);
|
|
167
|
+
}
|
|
168
|
+
if (Buffer.isBuffer(encoded)) {
|
|
169
|
+
return encoded;
|
|
170
|
+
}
|
|
171
|
+
if (encoded instanceof ArrayBuffer) {
|
|
172
|
+
return Buffer.from(encoded);
|
|
173
|
+
}
|
|
174
|
+
if (ArrayBuffer.isView(encoded)) {
|
|
175
|
+
const view = encoded;
|
|
176
|
+
return Buffer.from(view.buffer, view.byteOffset, view.byteLength);
|
|
177
|
+
}
|
|
178
|
+
if (typeof encoded === "string") {
|
|
179
|
+
return Buffer.from(encoded, "utf-8");
|
|
180
|
+
}
|
|
181
|
+
if (typeof encoded === "number" || typeof encoded === "boolean" || typeof encoded === "bigint") {
|
|
182
|
+
return Buffer.from(JSON.stringify(encoded), "utf-8");
|
|
183
|
+
}
|
|
184
|
+
if (typeof encoded === "object") {
|
|
185
|
+
return Buffer.from(JSON.stringify(encoded), "utf-8");
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Unsupported payload type for ${context}`);
|
|
188
|
+
}
|
|
189
|
+
function createWebSocketSchemas(schemaValidator, eventSchemas) {
|
|
190
|
+
const clientMessagesSchema = buildUnionSchema(
|
|
191
|
+
schemaValidator,
|
|
192
|
+
eventSchemas.clientMessages
|
|
193
|
+
);
|
|
194
|
+
const serverMessagesSchema = buildUnionSchema(
|
|
195
|
+
schemaValidator,
|
|
196
|
+
eventSchemas.serverMessages
|
|
197
|
+
);
|
|
198
|
+
const errorsSchema = buildUnionSchema(
|
|
199
|
+
schemaValidator,
|
|
200
|
+
eventSchemas.errors
|
|
201
|
+
);
|
|
202
|
+
const pingSchema = eventSchemas.ping?.shape;
|
|
203
|
+
const pongSchema = eventSchemas.pong?.shape;
|
|
204
|
+
const closeReasonSchema = eventSchemas.closeReason ? buildUnionSchema(schemaValidator, eventSchemas.closeReason) : void 0;
|
|
205
|
+
return {
|
|
206
|
+
clientMessagesSchema,
|
|
207
|
+
serverMessagesSchema,
|
|
208
|
+
errorsSchema,
|
|
209
|
+
pingSchema,
|
|
210
|
+
pongSchema,
|
|
211
|
+
closeReasonSchema
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export {
|
|
215
|
+
buildUnionSchema,
|
|
216
|
+
createWebSocketSchemas,
|
|
217
|
+
decodeSchemaValue,
|
|
218
|
+
encodeSchemaValue,
|
|
219
|
+
generateAsyncApi,
|
|
220
|
+
normalizeEncodedValue,
|
|
221
|
+
parseSchemaValue
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=index.mjs.map
|