@jaypie/fabric 0.1.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.
Files changed (166) hide show
  1. package/README.md +677 -0
  2. package/dist/cjs/commander/FabricCommander.d.ts +94 -0
  3. package/dist/cjs/commander/createCommanderOptions.d.ts +25 -0
  4. package/dist/cjs/commander/fabricCommand.d.ts +43 -0
  5. package/dist/cjs/commander/index.cjs +1487 -0
  6. package/dist/cjs/commander/index.cjs.map +1 -0
  7. package/dist/cjs/commander/index.d.ts +6 -0
  8. package/dist/cjs/commander/parseCommanderOptions.d.ts +32 -0
  9. package/dist/cjs/commander/registerServiceCommand.d.ts +43 -0
  10. package/dist/cjs/commander/types.d.ts +107 -0
  11. package/dist/cjs/constants.d.ts +12 -0
  12. package/dist/cjs/convert-date.d.ts +47 -0
  13. package/dist/cjs/convert.d.ts +69 -0
  14. package/dist/cjs/data/FabricData.d.ts +42 -0
  15. package/dist/cjs/data/index.cjs +1575 -0
  16. package/dist/cjs/data/index.cjs.map +1 -0
  17. package/dist/cjs/data/index.d.ts +5 -0
  18. package/dist/cjs/data/services/archive.d.ts +8 -0
  19. package/dist/cjs/data/services/create.d.ts +8 -0
  20. package/dist/cjs/data/services/delete.d.ts +8 -0
  21. package/dist/cjs/data/services/execute.d.ts +8 -0
  22. package/dist/cjs/data/services/index.d.ts +7 -0
  23. package/dist/cjs/data/services/list.d.ts +8 -0
  24. package/dist/cjs/data/services/read.d.ts +8 -0
  25. package/dist/cjs/data/services/update.d.ts +8 -0
  26. package/dist/cjs/data/transforms.d.ts +80 -0
  27. package/dist/cjs/data/types.d.ts +190 -0
  28. package/dist/cjs/express/FabricRouter.d.ts +29 -0
  29. package/dist/cjs/express/fabricExpress.d.ts +16 -0
  30. package/dist/cjs/express/index.cjs +505 -0
  31. package/dist/cjs/express/index.cjs.map +1 -0
  32. package/dist/cjs/express/index.d.ts +3 -0
  33. package/dist/cjs/express/types.d.ts +51 -0
  34. package/dist/cjs/helpers/fallback.d.ts +21 -0
  35. package/dist/cjs/helpers/index.d.ts +3 -0
  36. package/dist/cjs/helpers/resolvedName.d.ts +24 -0
  37. package/dist/cjs/http/FabricHttpServer.d.ts +31 -0
  38. package/dist/cjs/http/authorization.d.ts +30 -0
  39. package/dist/cjs/http/cors.d.ts +40 -0
  40. package/dist/cjs/http/fabricHttp.d.ts +28 -0
  41. package/dist/cjs/http/httpTransform.d.ts +36 -0
  42. package/dist/cjs/http/index.cjs +1820 -0
  43. package/dist/cjs/http/index.cjs.map +1 -0
  44. package/dist/cjs/http/index.d.ts +10 -0
  45. package/dist/cjs/http/stream.d.ts +185 -0
  46. package/dist/cjs/http/types.d.ts +343 -0
  47. package/dist/cjs/index/index.d.ts +8 -0
  48. package/dist/cjs/index/keyBuilder.d.ts +81 -0
  49. package/dist/cjs/index/registry.d.ts +56 -0
  50. package/dist/cjs/index/types.d.ts +54 -0
  51. package/dist/cjs/index.cjs +1674 -0
  52. package/dist/cjs/index.cjs.map +1 -0
  53. package/dist/cjs/index.d.ts +18 -0
  54. package/dist/cjs/lambda/createLambdaService.d.ts +33 -0
  55. package/dist/cjs/lambda/fabricLambda.d.ts +36 -0
  56. package/dist/cjs/lambda/index.cjs +967 -0
  57. package/dist/cjs/lambda/index.cjs.map +1 -0
  58. package/dist/cjs/lambda/index.d.ts +2 -0
  59. package/dist/cjs/lambda/types.d.ts +68 -0
  60. package/dist/cjs/llm/createLlmTool.d.ts +40 -0
  61. package/dist/cjs/llm/fabricTool.d.ts +40 -0
  62. package/dist/cjs/llm/index.cjs +1107 -0
  63. package/dist/cjs/llm/index.cjs.map +1 -0
  64. package/dist/cjs/llm/index.d.ts +3 -0
  65. package/dist/cjs/llm/inputToJsonSchema.d.ts +32 -0
  66. package/dist/cjs/llm/types.d.ts +61 -0
  67. package/dist/cjs/mcp/fabricMcp.d.ts +38 -0
  68. package/dist/cjs/mcp/index.cjs +938 -0
  69. package/dist/cjs/mcp/index.cjs.map +1 -0
  70. package/dist/cjs/mcp/index.d.ts +2 -0
  71. package/dist/cjs/mcp/registerMcpTool.d.ts +38 -0
  72. package/dist/cjs/mcp/types.d.ts +60 -0
  73. package/dist/cjs/models/base.d.ts +209 -0
  74. package/dist/cjs/resolve-date.d.ts +47 -0
  75. package/dist/cjs/resolve.d.ts +69 -0
  76. package/dist/cjs/resolveService.d.ts +49 -0
  77. package/dist/cjs/service.d.ts +13 -0
  78. package/dist/cjs/status.d.ts +30 -0
  79. package/dist/cjs/types/elementaryTypes.d.ts +84 -0
  80. package/dist/cjs/types/fieldCategory.d.ts +20 -0
  81. package/dist/cjs/types/fieldDefinition.d.ts +46 -0
  82. package/dist/cjs/types/index.d.ts +4 -0
  83. package/dist/cjs/types.d.ts +56 -0
  84. package/dist/esm/commander/FabricCommander.d.ts +94 -0
  85. package/dist/esm/commander/createCommanderOptions.d.ts +25 -0
  86. package/dist/esm/commander/fabricCommand.d.ts +43 -0
  87. package/dist/esm/commander/index.d.ts +6 -0
  88. package/dist/esm/commander/index.js +1482 -0
  89. package/dist/esm/commander/index.js.map +1 -0
  90. package/dist/esm/commander/parseCommanderOptions.d.ts +32 -0
  91. package/dist/esm/commander/registerServiceCommand.d.ts +43 -0
  92. package/dist/esm/commander/types.d.ts +107 -0
  93. package/dist/esm/constants.d.ts +12 -0
  94. package/dist/esm/convert-date.d.ts +47 -0
  95. package/dist/esm/convert.d.ts +69 -0
  96. package/dist/esm/data/FabricData.d.ts +42 -0
  97. package/dist/esm/data/index.d.ts +5 -0
  98. package/dist/esm/data/index.js +1548 -0
  99. package/dist/esm/data/index.js.map +1 -0
  100. package/dist/esm/data/services/archive.d.ts +8 -0
  101. package/dist/esm/data/services/create.d.ts +8 -0
  102. package/dist/esm/data/services/delete.d.ts +8 -0
  103. package/dist/esm/data/services/execute.d.ts +8 -0
  104. package/dist/esm/data/services/index.d.ts +7 -0
  105. package/dist/esm/data/services/list.d.ts +8 -0
  106. package/dist/esm/data/services/read.d.ts +8 -0
  107. package/dist/esm/data/services/update.d.ts +8 -0
  108. package/dist/esm/data/transforms.d.ts +80 -0
  109. package/dist/esm/data/types.d.ts +190 -0
  110. package/dist/esm/express/FabricRouter.d.ts +29 -0
  111. package/dist/esm/express/fabricExpress.d.ts +16 -0
  112. package/dist/esm/express/index.d.ts +3 -0
  113. package/dist/esm/express/index.js +500 -0
  114. package/dist/esm/express/index.js.map +1 -0
  115. package/dist/esm/express/types.d.ts +51 -0
  116. package/dist/esm/helpers/fallback.d.ts +21 -0
  117. package/dist/esm/helpers/index.d.ts +3 -0
  118. package/dist/esm/helpers/resolvedName.d.ts +24 -0
  119. package/dist/esm/http/FabricHttpServer.d.ts +31 -0
  120. package/dist/esm/http/authorization.d.ts +30 -0
  121. package/dist/esm/http/cors.d.ts +40 -0
  122. package/dist/esm/http/fabricHttp.d.ts +28 -0
  123. package/dist/esm/http/httpTransform.d.ts +36 -0
  124. package/dist/esm/http/index.d.ts +10 -0
  125. package/dist/esm/http/index.js +1775 -0
  126. package/dist/esm/http/index.js.map +1 -0
  127. package/dist/esm/http/stream.d.ts +185 -0
  128. package/dist/esm/http/types.d.ts +343 -0
  129. package/dist/esm/index/index.d.ts +8 -0
  130. package/dist/esm/index/keyBuilder.d.ts +81 -0
  131. package/dist/esm/index/registry.d.ts +56 -0
  132. package/dist/esm/index/types.d.ts +54 -0
  133. package/dist/esm/index.d.ts +18 -0
  134. package/dist/esm/index.js +1606 -0
  135. package/dist/esm/index.js.map +1 -0
  136. package/dist/esm/lambda/createLambdaService.d.ts +33 -0
  137. package/dist/esm/lambda/fabricLambda.d.ts +36 -0
  138. package/dist/esm/lambda/index.d.ts +2 -0
  139. package/dist/esm/lambda/index.js +965 -0
  140. package/dist/esm/lambda/index.js.map +1 -0
  141. package/dist/esm/lambda/types.d.ts +68 -0
  142. package/dist/esm/llm/createLlmTool.d.ts +40 -0
  143. package/dist/esm/llm/fabricTool.d.ts +40 -0
  144. package/dist/esm/llm/index.d.ts +3 -0
  145. package/dist/esm/llm/index.js +1104 -0
  146. package/dist/esm/llm/index.js.map +1 -0
  147. package/dist/esm/llm/inputToJsonSchema.d.ts +32 -0
  148. package/dist/esm/llm/types.d.ts +61 -0
  149. package/dist/esm/mcp/fabricMcp.d.ts +38 -0
  150. package/dist/esm/mcp/index.d.ts +2 -0
  151. package/dist/esm/mcp/index.js +936 -0
  152. package/dist/esm/mcp/index.js.map +1 -0
  153. package/dist/esm/mcp/registerMcpTool.d.ts +38 -0
  154. package/dist/esm/mcp/types.d.ts +60 -0
  155. package/dist/esm/models/base.d.ts +209 -0
  156. package/dist/esm/resolve-date.d.ts +47 -0
  157. package/dist/esm/resolve.d.ts +69 -0
  158. package/dist/esm/resolveService.d.ts +49 -0
  159. package/dist/esm/service.d.ts +13 -0
  160. package/dist/esm/status.d.ts +30 -0
  161. package/dist/esm/types/elementaryTypes.d.ts +84 -0
  162. package/dist/esm/types/fieldCategory.d.ts +20 -0
  163. package/dist/esm/types/fieldDefinition.d.ts +46 -0
  164. package/dist/esm/types/index.d.ts +4 -0
  165. package/dist/esm/types.d.ts +56 -0
  166. package/package.json +122 -0
@@ -0,0 +1,1820 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@jaypie/errors');
4
+
5
+ /**
6
+ * Meta-modeling Constants
7
+ */
8
+ // =============================================================================
9
+ // Constants
10
+ // =============================================================================
11
+ /** Root organizational unit */
12
+ /** Fabric version - used to identify pre-instantiated Services */
13
+ const FABRIC_VERSION = "0.1.0";
14
+
15
+ /**
16
+ * Date Type Conversion for @jaypie/fabric
17
+ *
18
+ * Adds Date as a supported type in the fabric type system.
19
+ * Follows the same conversion patterns as String, Number, Boolean.
20
+ */
21
+ /**
22
+ * Convert a value to a Date
23
+ *
24
+ * Supported inputs:
25
+ * - Date: returned as-is (validated)
26
+ * - Number: treated as Unix timestamp (milliseconds)
27
+ * - String: parsed via Date constructor (ISO 8601, etc.)
28
+ * - Object with value property: unwrapped and converted
29
+ *
30
+ * @throws BadRequestError if value cannot be converted to valid Date
31
+ */
32
+ function fabricDate(value) {
33
+ // Already a Date
34
+ if (value instanceof Date) {
35
+ if (Number.isNaN(value.getTime())) {
36
+ throw new errors.BadRequestError("Invalid Date value");
37
+ }
38
+ return value;
39
+ }
40
+ // Null/undefined
41
+ if (value === null || value === undefined) {
42
+ throw new errors.BadRequestError("Cannot convert null or undefined to Date");
43
+ }
44
+ // Object with value property (fabric pattern)
45
+ if (typeof value === "object" && value !== null && "value" in value) {
46
+ return fabricDate(value.value);
47
+ }
48
+ // Number (timestamp in milliseconds)
49
+ if (typeof value === "number") {
50
+ if (Number.isNaN(value)) {
51
+ throw new errors.BadRequestError("Cannot convert NaN to Date");
52
+ }
53
+ const date = new Date(value);
54
+ if (Number.isNaN(date.getTime())) {
55
+ throw new errors.BadRequestError(`Cannot convert ${value} to Date`);
56
+ }
57
+ return date;
58
+ }
59
+ // String (ISO 8601 or parseable format)
60
+ if (typeof value === "string") {
61
+ // Empty string is invalid
62
+ if (value.trim() === "") {
63
+ throw new errors.BadRequestError("Cannot convert empty string to Date");
64
+ }
65
+ const date = new Date(value);
66
+ if (Number.isNaN(date.getTime())) {
67
+ throw new errors.BadRequestError(`Cannot convert "${value}" to Date`);
68
+ }
69
+ return date;
70
+ }
71
+ // Boolean cannot be converted to Date
72
+ if (typeof value === "boolean") {
73
+ throw new errors.BadRequestError("Cannot convert boolean to Date");
74
+ }
75
+ // Arrays - attempt single element extraction
76
+ if (Array.isArray(value)) {
77
+ if (value.length === 1) {
78
+ return fabricDate(value[0]);
79
+ }
80
+ throw new errors.BadRequestError(`Cannot convert array with ${value.length} elements to Date`);
81
+ }
82
+ throw new errors.BadRequestError(`Cannot convert ${typeof value} to Date`);
83
+ }
84
+ /**
85
+ * Type guard for Date type in schema definitions
86
+ */
87
+ function isDateType(type) {
88
+ return type === Date;
89
+ }
90
+
91
+ // Fabric functions for @jaypie/fabric
92
+ /**
93
+ * Try to parse a string as JSON if it looks like JSON
94
+ * Returns the parsed value or the original string if not JSON
95
+ */
96
+ function tryParseJson(value) {
97
+ const trimmed = value.trim();
98
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
99
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
100
+ try {
101
+ return JSON.parse(trimmed);
102
+ }
103
+ catch {
104
+ // Not valid JSON, return original
105
+ return value;
106
+ }
107
+ }
108
+ return value;
109
+ }
110
+ /**
111
+ * Unwrap arrays and objects to get to the scalar value
112
+ * - Single-element arrays unwrap to their element
113
+ * - Objects with value property unwrap to that value
114
+ * - Recursively unwraps nested structures
115
+ */
116
+ function unwrapToScalar(value) {
117
+ if (value === undefined || value === null) {
118
+ return value;
119
+ }
120
+ // Unwrap single-element arrays
121
+ if (Array.isArray(value)) {
122
+ if (value.length === 0) {
123
+ return undefined;
124
+ }
125
+ if (value.length === 1) {
126
+ return unwrapToScalar(value[0]);
127
+ }
128
+ throw new errors.BadRequestError("Cannot convert multi-value array to scalar");
129
+ }
130
+ // Unwrap objects with value property
131
+ if (typeof value === "object") {
132
+ const obj = value;
133
+ if ("value" in obj) {
134
+ return unwrapToScalar(obj.value);
135
+ }
136
+ throw new errors.BadRequestError("Object must have a value attribute");
137
+ }
138
+ return value;
139
+ }
140
+ /**
141
+ * Prepare a value for scalar conversion by parsing JSON strings and unwrapping
142
+ */
143
+ function prepareForScalarConversion(value) {
144
+ if (value === undefined || value === null) {
145
+ return value;
146
+ }
147
+ // Try to parse JSON strings
148
+ if (typeof value === "string") {
149
+ const parsed = tryParseJson(value);
150
+ if (parsed !== value) {
151
+ // Successfully parsed, unwrap the result
152
+ return unwrapToScalar(parsed);
153
+ }
154
+ return value;
155
+ }
156
+ // Unwrap arrays and objects
157
+ if (Array.isArray(value) || typeof value === "object") {
158
+ return unwrapToScalar(value);
159
+ }
160
+ return value;
161
+ }
162
+ /**
163
+ * Convert a value to a boolean
164
+ * - Arrays, objects, and JSON strings are unwrapped first
165
+ * - String "true" becomes true
166
+ * - String "false" becomes false
167
+ * - Strings that parse to numbers: positive = true, zero or negative = false
168
+ * - Numbers: positive = true, zero or negative = false
169
+ * - Boolean passes through
170
+ */
171
+ function fabricBoolean(value) {
172
+ // Prepare value by parsing JSON and unwrapping arrays/objects
173
+ const prepared = prepareForScalarConversion(value);
174
+ if (prepared === undefined || prepared === null) {
175
+ return undefined;
176
+ }
177
+ if (typeof prepared === "boolean") {
178
+ return prepared;
179
+ }
180
+ if (typeof prepared === "string") {
181
+ if (prepared === "") {
182
+ return undefined;
183
+ }
184
+ const lower = prepared.toLowerCase();
185
+ if (lower === "true") {
186
+ return true;
187
+ }
188
+ if (lower === "false") {
189
+ return false;
190
+ }
191
+ // Try to parse as number
192
+ const num = parseFloat(prepared);
193
+ if (isNaN(num)) {
194
+ throw new errors.BadRequestError(`Cannot convert "${prepared}" to Boolean`);
195
+ }
196
+ return num > 0;
197
+ }
198
+ if (typeof prepared === "number") {
199
+ if (isNaN(prepared)) {
200
+ throw new errors.BadRequestError("Cannot convert NaN to Boolean");
201
+ }
202
+ return prepared > 0;
203
+ }
204
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to Boolean`);
205
+ }
206
+ /**
207
+ * Convert a value to a number
208
+ * - Arrays, objects, and JSON strings are unwrapped first
209
+ * - String "" becomes undefined
210
+ * - String "true" becomes 1
211
+ * - String "false" becomes 0
212
+ * - Strings that parse to numbers use those values
213
+ * - Strings that parse to NaN throw BadRequestError
214
+ * - Boolean true becomes 1, false becomes 0
215
+ * - Number passes through
216
+ */
217
+ function fabricNumber(value) {
218
+ // Prepare value by parsing JSON and unwrapping arrays/objects
219
+ const prepared = prepareForScalarConversion(value);
220
+ if (prepared === undefined || prepared === null) {
221
+ return undefined;
222
+ }
223
+ if (typeof prepared === "number") {
224
+ if (isNaN(prepared)) {
225
+ throw new errors.BadRequestError("Cannot convert NaN to Number");
226
+ }
227
+ return prepared;
228
+ }
229
+ if (typeof prepared === "boolean") {
230
+ return prepared ? 1 : 0;
231
+ }
232
+ if (typeof prepared === "string") {
233
+ if (prepared === "") {
234
+ return undefined;
235
+ }
236
+ const lower = prepared.toLowerCase();
237
+ if (lower === "true") {
238
+ return 1;
239
+ }
240
+ if (lower === "false") {
241
+ return 0;
242
+ }
243
+ const num = parseFloat(prepared);
244
+ if (isNaN(num)) {
245
+ throw new errors.BadRequestError(`Cannot convert "${prepared}" to Number`);
246
+ }
247
+ return num;
248
+ }
249
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to Number`);
250
+ }
251
+ /**
252
+ * Convert a value to a string
253
+ * - Arrays, objects, and JSON strings are unwrapped first
254
+ * - String "" becomes undefined
255
+ * - Boolean true becomes "true", false becomes "false"
256
+ * - Number converts to string representation
257
+ * - String passes through
258
+ */
259
+ function fabricString(value) {
260
+ // Prepare value by parsing JSON and unwrapping arrays/objects
261
+ const prepared = prepareForScalarConversion(value);
262
+ if (prepared === undefined || prepared === null) {
263
+ return undefined;
264
+ }
265
+ if (typeof prepared === "string") {
266
+ if (prepared === "") {
267
+ return undefined;
268
+ }
269
+ return prepared;
270
+ }
271
+ if (typeof prepared === "boolean") {
272
+ return prepared ? "true" : "false";
273
+ }
274
+ if (typeof prepared === "number") {
275
+ if (isNaN(prepared)) {
276
+ throw new errors.BadRequestError("Cannot convert NaN to String");
277
+ }
278
+ return String(prepared);
279
+ }
280
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to String`);
281
+ }
282
+ /**
283
+ * Convert a value to an array
284
+ * - Non-arrays become arrays containing that value
285
+ * - Arrays of a single value become that value (unwrapped)
286
+ * - Multi-value arrays throw BadRequestError
287
+ * - undefined/null become undefined
288
+ */
289
+ function fabricArray(value) {
290
+ if (value === undefined || value === null) {
291
+ return undefined;
292
+ }
293
+ if (Array.isArray(value)) {
294
+ // Arrays pass through (single-element unwrapping happens when converting FROM array)
295
+ return value;
296
+ }
297
+ // Non-arrays become single-element arrays
298
+ return [value];
299
+ }
300
+ /**
301
+ * Convert a value to an object with a value property
302
+ * - Scalars become { value: scalar }
303
+ * - Arrays become { value: array }
304
+ * - Objects with a value attribute pass through
305
+ * - Objects without a value attribute throw BadRequestError
306
+ * - undefined/null become undefined
307
+ */
308
+ function fabricObject(value) {
309
+ if (value === undefined || value === null) {
310
+ return undefined;
311
+ }
312
+ // Check if already an object (but not an array)
313
+ if (typeof value === "object" && !Array.isArray(value)) {
314
+ const obj = value;
315
+ if ("value" in obj) {
316
+ return obj;
317
+ }
318
+ throw new errors.BadRequestError("Object must have a value attribute");
319
+ }
320
+ // Scalars and arrays become { value: ... }
321
+ return { value };
322
+ }
323
+ /**
324
+ * Check if a type is a typed array (e.g., [String], [Number], [], etc.)
325
+ */
326
+ function isTypedArrayType(type) {
327
+ return Array.isArray(type);
328
+ }
329
+ /**
330
+ * Split a string on comma or tab delimiters for typed array conversion.
331
+ * Only splits if the string contains commas or tabs.
332
+ * Returns the original value if not a string or no delimiters found.
333
+ */
334
+ function splitStringForArray(value) {
335
+ if (typeof value !== "string") {
336
+ return value;
337
+ }
338
+ // Check for comma or tab delimiters
339
+ if (value.includes(",")) {
340
+ return value.split(",").map((s) => s.trim());
341
+ }
342
+ if (value.includes("\t")) {
343
+ return value.split("\t").map((s) => s.trim());
344
+ }
345
+ return value;
346
+ }
347
+ /**
348
+ * Try to parse a string as JSON for array context.
349
+ * Returns parsed value if it's an array, otherwise returns original.
350
+ */
351
+ function tryParseJsonArray(value) {
352
+ if (typeof value !== "string") {
353
+ return value;
354
+ }
355
+ const trimmed = value.trim();
356
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
357
+ try {
358
+ const parsed = JSON.parse(trimmed);
359
+ if (Array.isArray(parsed)) {
360
+ return parsed;
361
+ }
362
+ }
363
+ catch {
364
+ // Not valid JSON, fall through
365
+ }
366
+ }
367
+ return value;
368
+ }
369
+ /**
370
+ * Get the element type from a typed array type
371
+ * Returns undefined for untyped arrays ([])
372
+ */
373
+ function getArrayElementType(type) {
374
+ if (type.length === 0) {
375
+ return undefined; // Untyped array
376
+ }
377
+ const elementType = type[0];
378
+ // Handle constructor types
379
+ if (elementType === Boolean)
380
+ return "boolean";
381
+ if (elementType === Number)
382
+ return "number";
383
+ if (elementType === String)
384
+ return "string";
385
+ if (elementType === Object)
386
+ return "object";
387
+ // Handle string types
388
+ if (elementType === "boolean")
389
+ return "boolean";
390
+ if (elementType === "number")
391
+ return "number";
392
+ if (elementType === "string")
393
+ return "string";
394
+ if (elementType === "object")
395
+ return "object";
396
+ // Handle shorthand types
397
+ if (elementType === "")
398
+ return "string"; // "" shorthand for String
399
+ if (typeof elementType === "object" &&
400
+ elementType !== null &&
401
+ Object.keys(elementType).length === 0) {
402
+ return "object"; // {} shorthand for Object
403
+ }
404
+ throw new errors.BadRequestError(`Unknown array element type: ${String(elementType)}`);
405
+ }
406
+ /**
407
+ * Convert a value to a typed array
408
+ * - Tries to parse JSON arrays first
409
+ * - Splits strings on comma/tab if present
410
+ * - Wraps non-arrays in an array
411
+ * - Converts each element to the specified element type
412
+ */
413
+ function fabricTypedArray(value, elementType) {
414
+ // Try to parse JSON array first
415
+ let processed = tryParseJsonArray(value);
416
+ // If still a string, try to split on comma/tab
417
+ processed = splitStringForArray(processed);
418
+ // Convert to array (wraps non-arrays)
419
+ const array = fabricArray(processed);
420
+ if (array === undefined) {
421
+ return undefined;
422
+ }
423
+ // If no element type specified, return as-is
424
+ if (elementType === undefined) {
425
+ return array;
426
+ }
427
+ // Convert each element to the element type
428
+ return array.map((element, index) => {
429
+ try {
430
+ switch (elementType) {
431
+ case "boolean":
432
+ return fabricBoolean(element);
433
+ case "number":
434
+ return fabricNumber(element);
435
+ case "object":
436
+ return fabricObject(element);
437
+ case "string":
438
+ return fabricString(element);
439
+ default:
440
+ throw new errors.BadRequestError(`Unknown element type: ${elementType}`);
441
+ }
442
+ }
443
+ catch (error) {
444
+ if (error instanceof errors.BadRequestError) {
445
+ throw new errors.BadRequestError(`Cannot convert array element at index ${index}: ${error.message}`);
446
+ }
447
+ throw error;
448
+ }
449
+ });
450
+ }
451
+ /**
452
+ * Fabric a value to the specified type
453
+ */
454
+ function fabric(value, type) {
455
+ // Check for Date type first
456
+ if (isDateType(type)) {
457
+ return fabricDate(value);
458
+ }
459
+ // Check for typed array types
460
+ if (isTypedArrayType(type)) {
461
+ const elementType = getArrayElementType(type);
462
+ return fabricTypedArray(value, elementType);
463
+ }
464
+ const normalizedType = normalizeType(type);
465
+ switch (normalizedType) {
466
+ case "array":
467
+ return fabricArray(value);
468
+ case "boolean":
469
+ return fabricBoolean(value);
470
+ case "number":
471
+ return fabricNumber(value);
472
+ case "object":
473
+ return fabricObject(value);
474
+ case "string":
475
+ return fabricString(value);
476
+ default:
477
+ throw new errors.BadRequestError(`Unknown type: ${String(type)}`);
478
+ }
479
+ }
480
+ /**
481
+ * Normalize type to string representation
482
+ */
483
+ function normalizeType(type) {
484
+ if (type === Array || type === "array") {
485
+ return "array";
486
+ }
487
+ if (type === Boolean || type === "boolean") {
488
+ return "boolean";
489
+ }
490
+ if (type === Number || type === "number") {
491
+ return "number";
492
+ }
493
+ if (type === Object || type === "object") {
494
+ return "object";
495
+ }
496
+ if (type === String || type === "string") {
497
+ return "string";
498
+ }
499
+ throw new errors.BadRequestError(`Unknown type: ${String(type)}`);
500
+ }
501
+
502
+ // Service for @jaypie/fabric
503
+ /**
504
+ * Check if a single-element array is a typed array type constructor.
505
+ */
506
+ function isTypedArrayConstructor(element) {
507
+ return (element === Boolean ||
508
+ element === Number ||
509
+ element === String ||
510
+ element === Object ||
511
+ element === "boolean" ||
512
+ element === "number" ||
513
+ element === "string" ||
514
+ element === "object" ||
515
+ element === "" ||
516
+ (typeof element === "object" &&
517
+ element !== null &&
518
+ !(element instanceof RegExp) &&
519
+ Object.keys(element).length === 0));
520
+ }
521
+ /**
522
+ * Check if a type is a validated string type (array of string literals and/or RegExp).
523
+ * Distinguishes from typed arrays like [String], [Number], etc.
524
+ */
525
+ function isValidatedStringType(type) {
526
+ if (!Array.isArray(type)) {
527
+ return false;
528
+ }
529
+ // Empty array is untyped array, not validated string
530
+ if (type.length === 0) {
531
+ return false;
532
+ }
533
+ // Single-element arrays with type constructors are typed arrays
534
+ if (type.length === 1 && isTypedArrayConstructor(type[0])) {
535
+ return false;
536
+ }
537
+ // Check that all elements are strings or RegExp
538
+ return type.every((item) => typeof item === "string" || item instanceof RegExp);
539
+ }
540
+ /**
541
+ * Check if a type is a validated number type (array of number literals).
542
+ * Distinguishes from typed arrays like [Number], etc.
543
+ */
544
+ function isValidatedNumberType(type) {
545
+ if (!Array.isArray(type)) {
546
+ return false;
547
+ }
548
+ // Empty array is untyped array, not validated number
549
+ if (type.length === 0) {
550
+ return false;
551
+ }
552
+ // Single-element arrays with type constructors are typed arrays
553
+ if (type.length === 1 && isTypedArrayConstructor(type[0])) {
554
+ return false;
555
+ }
556
+ // Check that all elements are numbers
557
+ return type.every((item) => typeof item === "number");
558
+ }
559
+ /**
560
+ * Parse input string as JSON if it's a string
561
+ */
562
+ function parseInput(input) {
563
+ if (input === undefined || input === null) {
564
+ return {};
565
+ }
566
+ if (typeof input === "string") {
567
+ if (input === "") {
568
+ return {};
569
+ }
570
+ try {
571
+ const parsed = JSON.parse(input);
572
+ if (typeof parsed !== "object" ||
573
+ parsed === null ||
574
+ Array.isArray(parsed)) {
575
+ throw new errors.BadRequestError("Input must be an object");
576
+ }
577
+ return parsed;
578
+ }
579
+ catch (error) {
580
+ if (error instanceof errors.BadRequestError) {
581
+ throw error;
582
+ }
583
+ throw new errors.BadRequestError("Invalid JSON input");
584
+ }
585
+ }
586
+ if (typeof input === "object" && !Array.isArray(input)) {
587
+ return input;
588
+ }
589
+ throw new errors.BadRequestError("Input must be an object or JSON string");
590
+ }
591
+ /**
592
+ * Run validation on a value (supports async validators)
593
+ */
594
+ async function runValidation(value, validate, fieldName) {
595
+ if (typeof validate === "function") {
596
+ const result = await validate(value);
597
+ if (result === false) {
598
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
599
+ }
600
+ }
601
+ else if (validate instanceof RegExp) {
602
+ if (typeof value !== "string" || !validate.test(value)) {
603
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
604
+ }
605
+ }
606
+ else if (Array.isArray(validate)) {
607
+ // Check if value matches any item in the array
608
+ for (const item of validate) {
609
+ if (item instanceof RegExp) {
610
+ if (typeof value === "string" && item.test(value)) {
611
+ return; // Match found
612
+ }
613
+ }
614
+ else if (typeof item === "function") {
615
+ try {
616
+ const result = await item(value);
617
+ if (result !== false) {
618
+ return; // Match found
619
+ }
620
+ }
621
+ catch {
622
+ // Continue to next item
623
+ }
624
+ }
625
+ else if (value === item) {
626
+ return; // Scalar match found
627
+ }
628
+ }
629
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
630
+ }
631
+ }
632
+ /**
633
+ * Check if a field is required
634
+ * A field is required unless it has a default OR required is explicitly false
635
+ */
636
+ function isFieldRequired(definition) {
637
+ if (definition.required === false) {
638
+ return false;
639
+ }
640
+ if (definition.default !== undefined) {
641
+ return false;
642
+ }
643
+ return true;
644
+ }
645
+ /**
646
+ * Process a single field through conversion and validation
647
+ */
648
+ async function processField(fieldName, value, definition) {
649
+ // Apply default if value is undefined
650
+ let processedValue = value;
651
+ if (processedValue === undefined && definition.default !== undefined) {
652
+ processedValue = definition.default;
653
+ }
654
+ // Determine actual type and validation
655
+ let actualType = definition.type;
656
+ let validation = definition.validate;
657
+ // Handle bare RegExp shorthand: /regex/
658
+ if (definition.type instanceof RegExp) {
659
+ actualType = String;
660
+ validation = definition.type; // The RegExp becomes the validation
661
+ }
662
+ // Handle validated string shorthand: ["value1", "value2"] or [/regex/]
663
+ else if (isValidatedStringType(definition.type)) {
664
+ actualType = String;
665
+ validation = definition.type; // The array becomes the validation
666
+ }
667
+ // Handle validated number shorthand: [1, 2, 3]
668
+ else if (isValidatedNumberType(definition.type)) {
669
+ actualType = Number;
670
+ validation = definition.type; // The array becomes the validation
671
+ }
672
+ // Fabric to target type
673
+ const convertedValue = fabric(processedValue, actualType);
674
+ // Check if required field is missing
675
+ if (convertedValue === undefined && isFieldRequired(definition)) {
676
+ throw new errors.BadRequestError(`Missing required field "${fieldName}"`);
677
+ }
678
+ // Run validation if provided
679
+ if (validation !== undefined && convertedValue !== undefined) {
680
+ await runValidation(convertedValue, validation, fieldName);
681
+ }
682
+ return convertedValue;
683
+ }
684
+ /**
685
+ * Fabric a service function
686
+ *
687
+ * Service builds a function that initiates a "controller" step that:
688
+ * - Parses the input if it is a string to object
689
+ * - Fabrics each input field to its type
690
+ * - Calls the validation function or regular expression or checks the array
691
+ * - Calls the service function and returns the response
692
+ *
693
+ * The returned function has config properties for introspection.
694
+ */
695
+ function fabricService(config) {
696
+ const { input: inputDefinitions, service } = config;
697
+ const handler = async (rawInput, context) => {
698
+ // Parse input (handles string JSON)
699
+ const parsedInput = parseInput(rawInput);
700
+ // If no input definitions, pass through to service or return parsed input
701
+ if (!inputDefinitions) {
702
+ if (service) {
703
+ return service(parsedInput, context);
704
+ }
705
+ return parsedInput;
706
+ }
707
+ // Process all fields in parallel
708
+ const entries = Object.entries(inputDefinitions);
709
+ const processedValues = await Promise.all(entries.map(([fieldName, definition]) => processField(fieldName, parsedInput[fieldName], definition)));
710
+ // Build processed input object
711
+ const processedInput = {};
712
+ entries.forEach(([fieldName], index) => {
713
+ processedInput[fieldName] = processedValues[index];
714
+ });
715
+ // Return processed input if no service, otherwise call service
716
+ if (service) {
717
+ return service(processedInput, context);
718
+ }
719
+ return processedInput;
720
+ };
721
+ // Attach config properties directly to handler for flat access
722
+ const typedHandler = handler;
723
+ typedHandler.$fabric = FABRIC_VERSION;
724
+ if (config.alias !== undefined)
725
+ typedHandler.alias = config.alias;
726
+ if (config.description !== undefined)
727
+ typedHandler.description = config.description;
728
+ if (config.input !== undefined)
729
+ typedHandler.input = config.input;
730
+ if (config.service !== undefined)
731
+ typedHandler.service = config.service;
732
+ return typedHandler;
733
+ }
734
+
735
+ /**
736
+ * Extract token from Authorization header
737
+ * Removes "Bearer " prefix (case insensitive) and strips whitespace
738
+ *
739
+ * Examples:
740
+ * - "Bearer eyJhbGc..." → "eyJhbGc..."
741
+ * - "bearer eyJhbGc..." → "eyJhbGc..."
742
+ * - "BEARER eyJhbGc..." → "eyJhbGc..."
743
+ * - "eyJhbGc..." → "eyJhbGc..."
744
+ * - " eyJhbGc... " → "eyJhbGc..."
745
+ */
746
+ function extractToken(authHeader) {
747
+ if (!authHeader) {
748
+ return "";
749
+ }
750
+ let token = authHeader.trim();
751
+ // Remove "Bearer " prefix (case insensitive)
752
+ const bearerRegex = /^bearer\s+/i;
753
+ if (bearerRegex.test(token)) {
754
+ token = token.replace(bearerRegex, "");
755
+ }
756
+ return token.trim();
757
+ }
758
+ /**
759
+ * Get authorization header from Headers object
760
+ */
761
+ function getAuthHeader(headers) {
762
+ return headers.get("authorization");
763
+ }
764
+ /**
765
+ * Validate authorization and return auth context
766
+ *
767
+ * @param headers - Request headers
768
+ * @param config - Authorization configuration (function or false)
769
+ * @returns Auth context from the authorization function, or undefined if public
770
+ * @throws UnauthorizedError if authorization fails
771
+ */
772
+ async function validateAuthorization(headers, config) {
773
+ // Public endpoint - no authorization required
774
+ if (config === false) {
775
+ return undefined;
776
+ }
777
+ const authHeader = getAuthHeader(headers);
778
+ const token = extractToken(authHeader);
779
+ // If authorization is required but no token provided
780
+ if (!token) {
781
+ throw new errors.UnauthorizedError("Authorization header required");
782
+ }
783
+ // Call the authorization function
784
+ const authFunction = config;
785
+ return authFunction(token);
786
+ }
787
+ /**
788
+ * Check if authorization is required
789
+ */
790
+ function isAuthorizationRequired(config) {
791
+ return config !== false;
792
+ }
793
+
794
+ /**
795
+ * Default HTTP transformation function
796
+ * Merges query parameters with body (body takes precedence)
797
+ */
798
+ const defaultHttpTransform = ({ body, query, }) => {
799
+ const queryObject = Object.fromEntries(query.entries());
800
+ const bodyObject = typeof body === "object" && body !== null ? body : {};
801
+ return {
802
+ ...queryObject,
803
+ ...bodyObject,
804
+ };
805
+ };
806
+ /**
807
+ * Parse query string into URLSearchParams
808
+ */
809
+ function parseQueryString(queryString) {
810
+ // Remove leading ? if present
811
+ const normalized = queryString.startsWith("?")
812
+ ? queryString.slice(1)
813
+ : queryString;
814
+ return new URLSearchParams(normalized);
815
+ }
816
+ /**
817
+ * Parse path parameters from a URL path using a route pattern
818
+ * @param path - The actual URL path (e.g., "/users/123")
819
+ * @param pattern - The route pattern (e.g., "/users/:id")
820
+ * @returns Object with extracted parameters
821
+ */
822
+ function parsePathParams(path, pattern) {
823
+ const params = {};
824
+ const pathParts = path.split("/").filter(Boolean);
825
+ const patternParts = pattern.split("/").filter(Boolean);
826
+ for (let i = 0; i < patternParts.length; i++) {
827
+ const patternPart = patternParts[i];
828
+ const pathPart = pathParts[i];
829
+ if (patternPart.startsWith(":")) {
830
+ // Extract parameter name (remove : prefix and ? suffix for optional params)
831
+ const paramName = patternPart.slice(1).replace("?", "");
832
+ if (pathPart !== undefined) {
833
+ params[paramName] = pathPart;
834
+ }
835
+ }
836
+ }
837
+ return params;
838
+ }
839
+ /**
840
+ * Parse request body from string or return as-is if already parsed
841
+ */
842
+ function parseBody(body) {
843
+ if (typeof body === "string") {
844
+ try {
845
+ return JSON.parse(body);
846
+ }
847
+ catch {
848
+ // Return as-is if not valid JSON
849
+ return body;
850
+ }
851
+ }
852
+ return body;
853
+ }
854
+ /**
855
+ * Create HTTP context from raw request data
856
+ */
857
+ function createHttpContext(options) {
858
+ const { body = {}, headers = {}, method = "GET", path = "/", queryString = "", params = {}, } = options;
859
+ // Normalize headers to Headers object
860
+ const normalizedHeaders = headers instanceof Headers
861
+ ? headers
862
+ : new Headers(headers);
863
+ return {
864
+ body: parseBody(body),
865
+ headers: normalizedHeaders,
866
+ method: method.toUpperCase(),
867
+ path,
868
+ query: parseQueryString(queryString),
869
+ params,
870
+ };
871
+ }
872
+ /**
873
+ * Apply HTTP transformation to get service input
874
+ */
875
+ async function transformHttpToInput(context, transform = defaultHttpTransform) {
876
+ return transform(context);
877
+ }
878
+
879
+ /**
880
+ * Check if a value is a fabricService (has $fabric property)
881
+ */
882
+ function isFabricService(value) {
883
+ return (typeof value === "function" &&
884
+ "$fabric" in value &&
885
+ typeof value.$fabric === "string");
886
+ }
887
+ /**
888
+ * Create an HTTP-aware fabric service
889
+ *
890
+ * Extends fabricService with:
891
+ * - HTTP context transformation (body, headers, method, path, query, params)
892
+ * - Authorization handling (token extraction from Authorization header)
893
+ * - CORS configuration (enabled by default)
894
+ *
895
+ * Accepts either:
896
+ * - Inline service definition (with `service` function)
897
+ * - Pre-built `fabricService` instance (via `service` property)
898
+ */
899
+ function fabricHttp(config) {
900
+ const { authorization = false, cors = true, http = defaultHttpTransform, service: serviceConfig, stream = false, ...baseConfig } = config;
901
+ // Resolve the underlying service
902
+ let underlyingService;
903
+ if (isFabricService(serviceConfig)) {
904
+ // Pre-built fabricService - merge configs
905
+ underlyingService = serviceConfig;
906
+ // Merge base config properties from the pre-built service
907
+ if (baseConfig.alias === undefined &&
908
+ underlyingService.alias !== undefined) {
909
+ baseConfig.alias = underlyingService.alias;
910
+ }
911
+ if (baseConfig.description === undefined &&
912
+ underlyingService.description !== undefined) {
913
+ baseConfig.description = underlyingService.description;
914
+ }
915
+ if (baseConfig.input === undefined &&
916
+ underlyingService.input !== undefined) {
917
+ baseConfig.input = underlyingService.input;
918
+ }
919
+ }
920
+ else {
921
+ // Inline service definition or plain function
922
+ const serviceFunction = serviceConfig;
923
+ underlyingService = fabricService({
924
+ ...baseConfig,
925
+ service: serviceFunction,
926
+ });
927
+ }
928
+ // Create the HTTP handler that processes HTTP context
929
+ const httpHandler = async (input, context) => {
930
+ // If context has HTTP info, process authorization
931
+ // (HTTP context is added by the adapter layer like fabricExpress)
932
+ if (context?.http && authorization !== false) {
933
+ const authResult = await validateAuthorization(context.http.headers, authorization);
934
+ // Add auth result to context
935
+ context.auth = authResult;
936
+ }
937
+ // Call the underlying service
938
+ return underlyingService(input, context);
939
+ };
940
+ // Create the HTTP service with all properties
941
+ const httpService = httpHandler;
942
+ // Copy properties from config (which may have been merged with underlying service)
943
+ httpService.$fabric = underlyingService.$fabric;
944
+ // Use baseConfig values (which include overrides) or fall back to underlying service
945
+ const resolvedAlias = baseConfig.alias ?? underlyingService.alias;
946
+ const resolvedDescription = baseConfig.description ?? underlyingService.description;
947
+ const resolvedInput = baseConfig.input ?? underlyingService.input;
948
+ if (resolvedAlias !== undefined) {
949
+ httpService.alias = resolvedAlias;
950
+ }
951
+ if (resolvedDescription !== undefined) {
952
+ httpService.description = resolvedDescription;
953
+ }
954
+ if (resolvedInput !== undefined) {
955
+ httpService.input = resolvedInput;
956
+ }
957
+ if (underlyingService.service !== undefined) {
958
+ httpService.service = underlyingService.service;
959
+ }
960
+ // Add HTTP-specific properties
961
+ httpService.authorization = authorization;
962
+ httpService.cors = cors;
963
+ httpService.http = http;
964
+ httpService.stream = stream;
965
+ return httpService;
966
+ }
967
+ /**
968
+ * Check if a service is an HTTP service (has http, authorization, cors properties)
969
+ */
970
+ function isFabricHttpService(value) {
971
+ return (isFabricService(value) &&
972
+ "authorization" in value &&
973
+ "cors" in value &&
974
+ "http" in value &&
975
+ "stream" in value);
976
+ }
977
+
978
+ /**
979
+ * Default CORS configuration
980
+ */
981
+ const DEFAULT_CORS_CONFIG = {
982
+ origin: "*",
983
+ credentials: false,
984
+ headers: ["Content-Type", "Authorization"],
985
+ exposeHeaders: [],
986
+ maxAge: 86400, // 24 hours
987
+ };
988
+ /**
989
+ * Default allowed methods for CORS
990
+ */
991
+ const DEFAULT_CORS_METHODS = [
992
+ "GET",
993
+ "POST",
994
+ "DELETE",
995
+ "OPTIONS",
996
+ ];
997
+ /**
998
+ * Normalize CORS option to CorsConfig
999
+ * - true → default config
1000
+ * - false → disabled (returns undefined)
1001
+ * - CorsConfig → merged with defaults
1002
+ */
1003
+ function normalizeCorsConfig(option) {
1004
+ // Undefined or true → use defaults
1005
+ if (option === undefined || option === true) {
1006
+ return { ...DEFAULT_CORS_CONFIG };
1007
+ }
1008
+ // False → disabled
1009
+ if (option === false) {
1010
+ return undefined;
1011
+ }
1012
+ // Merge with defaults
1013
+ return {
1014
+ ...DEFAULT_CORS_CONFIG,
1015
+ ...option,
1016
+ };
1017
+ }
1018
+ /**
1019
+ * Get the allowed origin for a request
1020
+ * @param config - CORS configuration
1021
+ * @param requestOrigin - Origin header from request
1022
+ * @returns The origin to allow, or undefined if not allowed
1023
+ */
1024
+ function getAllowedOrigin(config, requestOrigin) {
1025
+ const { origin } = config;
1026
+ // Wildcard allows all
1027
+ if (origin === "*") {
1028
+ return "*";
1029
+ }
1030
+ // No origin in request
1031
+ if (!requestOrigin) {
1032
+ return undefined;
1033
+ }
1034
+ // Array of allowed origins
1035
+ if (Array.isArray(origin)) {
1036
+ if (origin.includes(requestOrigin)) {
1037
+ return requestOrigin;
1038
+ }
1039
+ return undefined;
1040
+ }
1041
+ // Single origin string
1042
+ if (origin === requestOrigin) {
1043
+ return requestOrigin;
1044
+ }
1045
+ return undefined;
1046
+ }
1047
+ /**
1048
+ * Build CORS headers for a response
1049
+ * @param config - CORS configuration
1050
+ * @param requestOrigin - Origin header from request
1051
+ * @param methods - Allowed HTTP methods
1052
+ * @returns Object with CORS headers, or empty object if CORS is disabled
1053
+ */
1054
+ function buildCorsHeaders(config, requestOrigin, methods = DEFAULT_CORS_METHODS) {
1055
+ if (!config) {
1056
+ return {};
1057
+ }
1058
+ const headers = {};
1059
+ // Access-Control-Allow-Origin
1060
+ const allowedOrigin = getAllowedOrigin(config, requestOrigin);
1061
+ if (allowedOrigin) {
1062
+ headers["Access-Control-Allow-Origin"] = allowedOrigin;
1063
+ }
1064
+ // Access-Control-Allow-Methods
1065
+ headers["Access-Control-Allow-Methods"] = methods.join(", ");
1066
+ // Access-Control-Allow-Headers
1067
+ if (config.headers && config.headers.length > 0) {
1068
+ headers["Access-Control-Allow-Headers"] = config.headers.join(", ");
1069
+ }
1070
+ // Access-Control-Allow-Credentials
1071
+ if (config.credentials) {
1072
+ headers["Access-Control-Allow-Credentials"] = "true";
1073
+ }
1074
+ // Access-Control-Expose-Headers
1075
+ if (config.exposeHeaders && config.exposeHeaders.length > 0) {
1076
+ headers["Access-Control-Expose-Headers"] = config.exposeHeaders.join(", ");
1077
+ }
1078
+ // Access-Control-Max-Age
1079
+ if (config.maxAge !== undefined) {
1080
+ headers["Access-Control-Max-Age"] = String(config.maxAge);
1081
+ }
1082
+ return headers;
1083
+ }
1084
+ /**
1085
+ * Check if request is a CORS preflight request
1086
+ */
1087
+ function isPreflightRequest(method, headers) {
1088
+ return (method.toUpperCase() === "OPTIONS" &&
1089
+ headers.has("access-control-request-method"));
1090
+ }
1091
+ /**
1092
+ * Build preflight response headers
1093
+ * Includes all CORS headers needed for preflight response
1094
+ */
1095
+ function buildPreflightHeaders(config, requestOrigin, requestMethod, requestHeaders, methods = DEFAULT_CORS_METHODS) {
1096
+ if (!config) {
1097
+ return {};
1098
+ }
1099
+ const headers = buildCorsHeaders(config, requestOrigin, methods);
1100
+ // Include requested headers in response if not already covered
1101
+ if (requestHeaders) {
1102
+ const requestedHeaders = requestHeaders.split(",").map((h) => h.trim());
1103
+ const allowedHeaders = config.headers || [];
1104
+ const combinedHeaders = [
1105
+ ...new Set([...allowedHeaders, ...requestedHeaders]),
1106
+ ];
1107
+ headers["Access-Control-Allow-Headers"] = combinedHeaders.join(", ");
1108
+ }
1109
+ return headers;
1110
+ }
1111
+
1112
+ /**
1113
+ * Default HTTP methods for fabric services
1114
+ */
1115
+ const DEFAULT_HTTP_METHODS = ["GET", "POST", "DELETE"];
1116
+ // #endregion
1117
+ // #region Streaming
1118
+ /**
1119
+ * HTTP stream event types for SSE/NDJSON streaming
1120
+ */
1121
+ exports.HttpStreamEventType = void 0;
1122
+ (function (HttpStreamEventType) {
1123
+ /** Stream complete */
1124
+ HttpStreamEventType["Complete"] = "complete";
1125
+ /** Final response data */
1126
+ HttpStreamEventType["Data"] = "data";
1127
+ /** Error event */
1128
+ HttpStreamEventType["Error"] = "error";
1129
+ /** Fabric progress message (from sendMessage) */
1130
+ HttpStreamEventType["Message"] = "message";
1131
+ /** Keep-alive signal (no content) */
1132
+ HttpStreamEventType["Noop"] = "noop";
1133
+ /** LLM text chunk */
1134
+ HttpStreamEventType["Text"] = "text";
1135
+ /** LLM tool call event */
1136
+ HttpStreamEventType["ToolCall"] = "tool_call";
1137
+ /** LLM tool result event */
1138
+ HttpStreamEventType["ToolResult"] = "tool_result";
1139
+ })(exports.HttpStreamEventType || (exports.HttpStreamEventType = {}));
1140
+ // #endregion
1141
+
1142
+ // FabricHttpServer - Standalone Lambda/server for multi-service HTTP routing
1143
+ /**
1144
+ * Check if event is API Gateway v2 format
1145
+ */
1146
+ function isApiGatewayV2Event(event) {
1147
+ return "requestContext" in event && "http" in event.requestContext;
1148
+ }
1149
+ /**
1150
+ * Check if entry is a FabricHttpServerRoute (has service property)
1151
+ */
1152
+ function isRouteConfig(entry) {
1153
+ return (typeof entry === "object" &&
1154
+ entry !== null &&
1155
+ "service" in entry &&
1156
+ isFabricHttpService(entry.service));
1157
+ }
1158
+ /**
1159
+ * Extract request data from API Gateway event (v1 or v2)
1160
+ */
1161
+ function extractRequestData(event) {
1162
+ if (isApiGatewayV2Event(event)) {
1163
+ // API Gateway v2 (HTTP API)
1164
+ return {
1165
+ body: event.body ?? null,
1166
+ headers: event.headers ?? {},
1167
+ method: event.requestContext.http.method,
1168
+ path: event.rawPath,
1169
+ queryString: event.rawQueryString ?? "",
1170
+ pathParams: event.pathParameters ?? {},
1171
+ };
1172
+ }
1173
+ else {
1174
+ // API Gateway v1 (REST API)
1175
+ const v1Event = event;
1176
+ // Convert query params to query string
1177
+ const queryParams = v1Event.queryStringParameters ?? {};
1178
+ const queryString = new URLSearchParams(queryParams).toString();
1179
+ return {
1180
+ body: v1Event.body,
1181
+ headers: v1Event.headers ?? {},
1182
+ method: v1Event.httpMethod,
1183
+ path: v1Event.path,
1184
+ queryString,
1185
+ pathParams: v1Event.pathParameters ?? {},
1186
+ };
1187
+ }
1188
+ }
1189
+ /**
1190
+ * Match a request path against a route pattern
1191
+ * Returns extracted params if matched, undefined if not
1192
+ */
1193
+ function matchRoute(requestPath, route) {
1194
+ const requestSegments = requestPath.split("/").filter(Boolean);
1195
+ const routeSegments = route.segments;
1196
+ // Check segment count (allow route to have optional params)
1197
+ if (requestSegments.length < routeSegments.filter((s) => !s.endsWith("?")).length) {
1198
+ return undefined;
1199
+ }
1200
+ if (requestSegments.length > routeSegments.length) {
1201
+ return undefined;
1202
+ }
1203
+ const params = {};
1204
+ for (let i = 0; i < routeSegments.length; i++) {
1205
+ const routeSegment = routeSegments[i];
1206
+ const requestSegment = requestSegments[i];
1207
+ if (routeSegment.startsWith(":")) {
1208
+ // Parameter segment
1209
+ const paramName = routeSegment.slice(1).replace("?", "");
1210
+ const isOptional = routeSegment.endsWith("?");
1211
+ if (requestSegment !== undefined) {
1212
+ params[paramName] = requestSegment;
1213
+ }
1214
+ else if (!isOptional) {
1215
+ return undefined; // Required param missing
1216
+ }
1217
+ }
1218
+ else {
1219
+ // Literal segment - must match exactly
1220
+ if (requestSegment !== routeSegment) {
1221
+ return undefined;
1222
+ }
1223
+ }
1224
+ }
1225
+ return params;
1226
+ }
1227
+ /**
1228
+ * Find matching route for a request
1229
+ */
1230
+ function findRoute(path, method, routes, prefix) {
1231
+ // Remove prefix from path if present
1232
+ let normalizedPath = path;
1233
+ if (prefix && normalizedPath.startsWith(prefix)) {
1234
+ normalizedPath = normalizedPath.slice(prefix.length) || "/";
1235
+ }
1236
+ // Find first matching route
1237
+ for (const route of routes) {
1238
+ // Check method first
1239
+ if (!route.methods.includes(method.toUpperCase())) {
1240
+ continue;
1241
+ }
1242
+ // Try to match path
1243
+ const params = matchRoute(normalizedPath, route);
1244
+ if (params !== undefined) {
1245
+ return { route, params };
1246
+ }
1247
+ }
1248
+ return undefined;
1249
+ }
1250
+ /**
1251
+ * Find route by path only (ignoring method) for 405 responses
1252
+ */
1253
+ function findRouteByPath(path, routes, prefix) {
1254
+ let normalizedPath = path;
1255
+ if (prefix && normalizedPath.startsWith(prefix)) {
1256
+ normalizedPath = normalizedPath.slice(prefix.length) || "/";
1257
+ }
1258
+ for (const route of routes) {
1259
+ const params = matchRoute(normalizedPath, route);
1260
+ if (params !== undefined) {
1261
+ return route;
1262
+ }
1263
+ }
1264
+ return undefined;
1265
+ }
1266
+ /**
1267
+ * Build API Gateway response
1268
+ */
1269
+ function buildResponse(statusCode, body, headers = {}) {
1270
+ return {
1271
+ statusCode,
1272
+ headers: {
1273
+ "Content-Type": "application/json",
1274
+ ...headers,
1275
+ },
1276
+ body: JSON.stringify(body),
1277
+ };
1278
+ }
1279
+ /**
1280
+ * Apply CORS headers to response headers object
1281
+ */
1282
+ function applyCorsHeaders(responseHeaders, corsHeaders) {
1283
+ for (const [key, value] of Object.entries(corsHeaders)) {
1284
+ if (value !== undefined) {
1285
+ responseHeaders[key] = value;
1286
+ }
1287
+ }
1288
+ }
1289
+ /**
1290
+ * Create a standalone HTTP server for Lambda
1291
+ *
1292
+ * Routes multiple FabricHttpService instances without Express:
1293
+ * - Parses API Gateway v1 and v2 events
1294
+ * - Matches routes by path pattern and HTTP method
1295
+ * - Handles CORS preflight and response headers
1296
+ * - Returns JSON:API formatted responses
1297
+ *
1298
+ * @example
1299
+ * ```typescript
1300
+ * import { FabricHttpServer } from "@jaypie/fabric/http";
1301
+ * import { lambdaHandler } from "@jaypie/lambda";
1302
+ *
1303
+ * const server = FabricHttpServer({
1304
+ * services: [
1305
+ * userService,
1306
+ * { service: productService, path: "/products/:id", methods: ["GET", "PUT"] },
1307
+ * ],
1308
+ * prefix: "/api",
1309
+ * });
1310
+ *
1311
+ * export const handler = lambdaHandler(server);
1312
+ * ```
1313
+ */
1314
+ function FabricHttpServer(config) {
1315
+ const { services, authorization: serverAuthorization, cors: serverCors = true, prefix, } = config;
1316
+ // Build routes from services
1317
+ const routes = [];
1318
+ const registeredServices = [];
1319
+ for (const entry of services) {
1320
+ let service;
1321
+ let path;
1322
+ let methods;
1323
+ if (isRouteConfig(entry)) {
1324
+ service = entry.service;
1325
+ path = entry.path;
1326
+ methods = entry.methods;
1327
+ }
1328
+ else if (isFabricHttpService(entry)) {
1329
+ service = entry;
1330
+ }
1331
+ else {
1332
+ throw new Error("FabricHttpServer: Each service entry must be a FabricHttpService or { service: FabricHttpService }");
1333
+ }
1334
+ // Determine path from config or service alias
1335
+ const routePath = path ?? (service.alias ? `/${service.alias}` : "/");
1336
+ // Parse route segments
1337
+ const segments = routePath.split("/").filter(Boolean);
1338
+ routes.push({
1339
+ path: routePath,
1340
+ segments,
1341
+ methods: methods ?? DEFAULT_HTTP_METHODS,
1342
+ service,
1343
+ });
1344
+ registeredServices.push(service);
1345
+ }
1346
+ // Normalize server-level CORS config
1347
+ const serverCorsConfig = normalizeCorsConfig(serverCors);
1348
+ /**
1349
+ * Main request handler
1350
+ */
1351
+ const handler = async (event) => {
1352
+ // Extract request data from API Gateway event
1353
+ const { body, headers, method, path: requestPath, queryString, pathParams } = extractRequestData(event);
1354
+ // Normalize headers to Headers object for consistency
1355
+ const headersObj = new Headers(headers);
1356
+ const origin = headersObj.get("origin");
1357
+ // Collect all methods for matched path (for 405 and OPTIONS)
1358
+ const pathRoute = findRouteByPath(requestPath, routes, prefix);
1359
+ const allowedMethods = pathRoute?.methods ?? [];
1360
+ // Handle CORS preflight
1361
+ if (isPreflightRequest(method, headersObj)) {
1362
+ const preflightHeaders = buildPreflightHeaders(serverCorsConfig, origin, headersObj.get("access-control-request-method"), headersObj.get("access-control-request-headers"), allowedMethods.length > 0 ? allowedMethods : DEFAULT_HTTP_METHODS);
1363
+ const responseHeaders = {};
1364
+ applyCorsHeaders(responseHeaders, preflightHeaders);
1365
+ return {
1366
+ statusCode: 204,
1367
+ headers: responseHeaders,
1368
+ body: "",
1369
+ };
1370
+ }
1371
+ // Build CORS headers for response
1372
+ const corsHeaders = buildCorsHeaders(serverCorsConfig, origin, allowedMethods);
1373
+ const responseHeaders = {
1374
+ "Content-Type": "application/json",
1375
+ };
1376
+ applyCorsHeaders(responseHeaders, corsHeaders);
1377
+ // Find matching route
1378
+ const match = findRoute(requestPath, method, routes, prefix);
1379
+ // 404 - No route found
1380
+ if (!match && !pathRoute) {
1381
+ const errorResponse = {
1382
+ errors: [
1383
+ {
1384
+ status: 404,
1385
+ title: "Not Found",
1386
+ detail: `No route matches ${requestPath}`,
1387
+ },
1388
+ ],
1389
+ };
1390
+ return buildResponse(404, errorResponse, responseHeaders);
1391
+ }
1392
+ // 405 - Path exists but method not allowed
1393
+ if (!match && pathRoute) {
1394
+ const errorResponse = {
1395
+ errors: [
1396
+ {
1397
+ status: 405,
1398
+ title: "Method Not Allowed",
1399
+ detail: `${method} is not allowed for ${requestPath}`,
1400
+ },
1401
+ ],
1402
+ };
1403
+ responseHeaders["Allow"] = pathRoute.methods.join(", ");
1404
+ return buildResponse(405, errorResponse, responseHeaders);
1405
+ }
1406
+ // We have a match
1407
+ const { route, params } = match;
1408
+ const service = route.service;
1409
+ try {
1410
+ // Create HTTP context
1411
+ const httpContext = createHttpContext({
1412
+ body,
1413
+ headers,
1414
+ method,
1415
+ path: requestPath,
1416
+ queryString,
1417
+ params: { ...pathParams, ...params },
1418
+ });
1419
+ // Transform to service input
1420
+ const input = await transformHttpToInput(httpContext, service.http);
1421
+ // Build service context
1422
+ const serviceContext = {
1423
+ http: httpContext,
1424
+ };
1425
+ // Call the service
1426
+ const result = await service(input, serviceContext);
1427
+ // Build success response
1428
+ const successResponse = {
1429
+ data: result,
1430
+ };
1431
+ return buildResponse(200, successResponse, responseHeaders);
1432
+ }
1433
+ catch (error) {
1434
+ // Handle errors
1435
+ const jaypieError = errors.isJaypieError(error);
1436
+ const status = jaypieError ? error.status : 500;
1437
+ const title = jaypieError
1438
+ ? error.title
1439
+ : "Internal Server Error";
1440
+ const detail = error instanceof Error ? error.message : String(error);
1441
+ const errorResponse = {
1442
+ errors: [
1443
+ {
1444
+ status,
1445
+ title,
1446
+ detail: status === 500 ? undefined : detail,
1447
+ },
1448
+ ],
1449
+ };
1450
+ return buildResponse(status, errorResponse, responseHeaders);
1451
+ }
1452
+ };
1453
+ // Attach metadata to handler
1454
+ const server = handler;
1455
+ server.services = registeredServices;
1456
+ server.prefix = prefix;
1457
+ server.routes = routes;
1458
+ return server;
1459
+ }
1460
+ /**
1461
+ * Check if a value is a FabricHttpServer
1462
+ */
1463
+ function isFabricHttpServer(value) {
1464
+ return (typeof value === "function" &&
1465
+ "services" in value &&
1466
+ Array.isArray(value.services) &&
1467
+ "routes" in value &&
1468
+ Array.isArray(value.routes));
1469
+ }
1470
+
1471
+ // #region Constants
1472
+ /**
1473
+ * Default stream configuration
1474
+ */
1475
+ const DEFAULT_STREAM_CONFIG = {
1476
+ format: "ndjson",
1477
+ heartbeat: 15000,
1478
+ includeTools: true,
1479
+ };
1480
+ // #endregion
1481
+ // #region Stream Config
1482
+ /**
1483
+ * Normalize stream option to StreamConfig
1484
+ * - true → default config (NDJSON format, tools included)
1485
+ * - false/undefined → disabled (returns undefined)
1486
+ */
1487
+ function normalizeStreamConfig(option) {
1488
+ if (option === true) {
1489
+ return { ...DEFAULT_STREAM_CONFIG };
1490
+ }
1491
+ return undefined;
1492
+ }
1493
+ /**
1494
+ * Check if streaming is enabled
1495
+ */
1496
+ function isStreamingEnabled(option) {
1497
+ return option === true;
1498
+ }
1499
+ // #endregion
1500
+ // #region Event Formatting
1501
+ /**
1502
+ * Format a stream event as SSE (Server-Sent Events)
1503
+ *
1504
+ * SSE format:
1505
+ * event: <type>
1506
+ * data: <json>
1507
+ *
1508
+ */
1509
+ function formatSseEvent(event) {
1510
+ const eventType = event.stream;
1511
+ const data = JSON.stringify(event);
1512
+ return `event: ${eventType}\ndata: ${data}\n\n`;
1513
+ }
1514
+ /**
1515
+ * Format a stream event as NDJSON (Newline-Delimited JSON)
1516
+ */
1517
+ function formatNdjsonEvent(event) {
1518
+ return JSON.stringify(event) + "\n";
1519
+ }
1520
+ /**
1521
+ * Format a stream event based on config
1522
+ */
1523
+ function formatStreamEvent(event, config) {
1524
+ if (config.format === "ndjson") {
1525
+ return formatNdjsonEvent(event);
1526
+ }
1527
+ return formatSseEvent(event);
1528
+ }
1529
+ /**
1530
+ * Get content-type header for stream format
1531
+ */
1532
+ function getStreamContentType(config) {
1533
+ if (config.format === "ndjson") {
1534
+ return "application/x-ndjson";
1535
+ }
1536
+ return "text/event-stream";
1537
+ }
1538
+ // #endregion
1539
+ // #region Event Creators
1540
+ /**
1541
+ * Create a message event (progress update)
1542
+ */
1543
+ function createMessageEvent(content, level) {
1544
+ const event = {
1545
+ stream: exports.HttpStreamEventType.Message,
1546
+ content,
1547
+ };
1548
+ if (level !== undefined) {
1549
+ event.level = level;
1550
+ }
1551
+ return event;
1552
+ }
1553
+ /**
1554
+ * Create a text event (LLM text chunk)
1555
+ */
1556
+ function createTextEvent(content) {
1557
+ return {
1558
+ stream: exports.HttpStreamEventType.Text,
1559
+ content,
1560
+ };
1561
+ }
1562
+ /**
1563
+ * Create a tool call event
1564
+ */
1565
+ function createToolCallEvent(toolCall) {
1566
+ return {
1567
+ stream: exports.HttpStreamEventType.ToolCall,
1568
+ toolCall,
1569
+ };
1570
+ }
1571
+ /**
1572
+ * Create a tool result event
1573
+ */
1574
+ function createToolResultEvent(toolResult) {
1575
+ return {
1576
+ stream: exports.HttpStreamEventType.ToolResult,
1577
+ toolResult,
1578
+ };
1579
+ }
1580
+ /**
1581
+ * Create a data event (final response)
1582
+ */
1583
+ function createDataEvent(data) {
1584
+ return {
1585
+ stream: exports.HttpStreamEventType.Data,
1586
+ data,
1587
+ };
1588
+ }
1589
+ /**
1590
+ * Create an error event
1591
+ */
1592
+ function createErrorEvent(error) {
1593
+ return {
1594
+ stream: exports.HttpStreamEventType.Error,
1595
+ error,
1596
+ };
1597
+ }
1598
+ /**
1599
+ * Create a complete event
1600
+ */
1601
+ function createCompleteEvent() {
1602
+ return {
1603
+ stream: exports.HttpStreamEventType.Complete,
1604
+ };
1605
+ }
1606
+ /**
1607
+ * Create a noop event (keep-alive signal)
1608
+ */
1609
+ function createNoopEvent() {
1610
+ return {
1611
+ stream: exports.HttpStreamEventType.Noop,
1612
+ };
1613
+ }
1614
+ /**
1615
+ * Create a stream context with streaming capabilities
1616
+ *
1617
+ * @param writer - Function to write stream events
1618
+ * @param baseContext - Base service context to extend
1619
+ * @returns Extended context with stream methods
1620
+ */
1621
+ function createStreamContext(writer, baseContext) {
1622
+ const streamEvent = (event) => {
1623
+ try {
1624
+ writer(event);
1625
+ }
1626
+ catch {
1627
+ // Swallow errors in stream writer (connection may be closed)
1628
+ }
1629
+ };
1630
+ const streamText = (content) => {
1631
+ streamEvent(createTextEvent(content));
1632
+ };
1633
+ // Create sendMessage that streams as message events
1634
+ const sendMessage = (message) => {
1635
+ try {
1636
+ const event = createMessageEvent(message.content, message.level);
1637
+ writer(event);
1638
+ }
1639
+ catch {
1640
+ // Swallow errors
1641
+ }
1642
+ };
1643
+ return {
1644
+ ...baseContext,
1645
+ sendMessage,
1646
+ streamEvent,
1647
+ streamText,
1648
+ };
1649
+ }
1650
+ // #endregion
1651
+ // #region Async Generator Utilities
1652
+ /**
1653
+ * Check if a value is an async iterable
1654
+ */
1655
+ function isAsyncIterable(value) {
1656
+ return (value !== null && typeof value === "object" && Symbol.asyncIterator in value);
1657
+ }
1658
+ /**
1659
+ * Collect all events from an async iterable into an array
1660
+ * Useful for testing
1661
+ */
1662
+ async function collectStreamEvents(stream) {
1663
+ const events = [];
1664
+ for await (const event of stream) {
1665
+ events.push(event);
1666
+ }
1667
+ return events;
1668
+ }
1669
+ /**
1670
+ * Create an async generator that yields events and returns final data
1671
+ */
1672
+ async function* wrapServiceForStreaming(serviceResult) {
1673
+ // If result is already an async iterable, pass through
1674
+ if (isAsyncIterable(serviceResult)) {
1675
+ yield* serviceResult;
1676
+ return;
1677
+ }
1678
+ // Otherwise, wrap single result as data event
1679
+ yield createDataEvent(serviceResult);
1680
+ yield createCompleteEvent();
1681
+ }
1682
+ // #endregion
1683
+ // #region LLM Stream Integration
1684
+ /**
1685
+ * LLM stream chunk types (matches @jaypie/llm LlmStreamChunkType)
1686
+ * Defined locally to avoid hard dependency on @jaypie/llm
1687
+ */
1688
+ const LLM_CHUNK_TYPES = {
1689
+ Done: "done",
1690
+ Error: "error",
1691
+ Text: "text",
1692
+ ToolCall: "tool_call",
1693
+ ToolResult: "tool_result",
1694
+ };
1695
+ /**
1696
+ * Convert an LLM stream chunk to HTTP stream event
1697
+ * Returns undefined for chunks that should be filtered out
1698
+ */
1699
+ function llmChunkToHttpEvent(chunk, options) {
1700
+ const { includeTools = false } = options ?? {};
1701
+ switch (chunk.type) {
1702
+ case LLM_CHUNK_TYPES.Text:
1703
+ if (chunk.content !== undefined) {
1704
+ return createTextEvent(chunk.content);
1705
+ }
1706
+ return undefined;
1707
+ case LLM_CHUNK_TYPES.ToolCall:
1708
+ if (!includeTools)
1709
+ return undefined;
1710
+ if (chunk.toolCall) {
1711
+ return createToolCallEvent(chunk.toolCall);
1712
+ }
1713
+ return undefined;
1714
+ case LLM_CHUNK_TYPES.ToolResult:
1715
+ if (!includeTools)
1716
+ return undefined;
1717
+ if (chunk.toolResult) {
1718
+ return createToolResultEvent(chunk.toolResult);
1719
+ }
1720
+ return undefined;
1721
+ case LLM_CHUNK_TYPES.Error:
1722
+ if (chunk.error) {
1723
+ return createErrorEvent(chunk.error);
1724
+ }
1725
+ return undefined;
1726
+ case LLM_CHUNK_TYPES.Done:
1727
+ return createCompleteEvent();
1728
+ default:
1729
+ // Unknown chunk type - skip
1730
+ return undefined;
1731
+ }
1732
+ }
1733
+ /**
1734
+ * Pipe an LLM stream (from @jaypie/llm Llm.stream()) to HTTP stream events
1735
+ *
1736
+ * @example
1737
+ * ```typescript
1738
+ * import Llm from "@jaypie/llm";
1739
+ * import { pipeLlmStream } from "@jaypie/fabric/http";
1740
+ *
1741
+ * const llmStream = Llm.stream("Tell me a story");
1742
+ * for await (const event of pipeLlmStream(llmStream)) {
1743
+ * yield event; // or writer(event)
1744
+ * }
1745
+ * ```
1746
+ */
1747
+ async function* pipeLlmStream(llmStream, options) {
1748
+ for await (const chunk of llmStream) {
1749
+ const event = llmChunkToHttpEvent(chunk, options);
1750
+ if (event !== undefined) {
1751
+ yield event;
1752
+ }
1753
+ }
1754
+ }
1755
+ /**
1756
+ * Pipe an LLM stream to a stream writer function
1757
+ *
1758
+ * @example
1759
+ * ```typescript
1760
+ * import Llm from "@jaypie/llm";
1761
+ * import { pipeLlmStreamToWriter } from "@jaypie/fabric/http";
1762
+ *
1763
+ * const llmStream = Llm.stream("Tell me a story");
1764
+ * await pipeLlmStreamToWriter(llmStream, (event) => {
1765
+ * res.write(formatSseEvent(event));
1766
+ * });
1767
+ * ```
1768
+ */
1769
+ async function pipeLlmStreamToWriter(llmStream, writer, options) {
1770
+ for await (const event of pipeLlmStream(llmStream, options)) {
1771
+ await writer(event);
1772
+ }
1773
+ }
1774
+ // #endregion
1775
+
1776
+ exports.DEFAULT_CORS_CONFIG = DEFAULT_CORS_CONFIG;
1777
+ exports.DEFAULT_CORS_METHODS = DEFAULT_CORS_METHODS;
1778
+ exports.DEFAULT_HTTP_METHODS = DEFAULT_HTTP_METHODS;
1779
+ exports.DEFAULT_STREAM_CONFIG = DEFAULT_STREAM_CONFIG;
1780
+ exports.FabricHttpServer = FabricHttpServer;
1781
+ exports.buildCorsHeaders = buildCorsHeaders;
1782
+ exports.buildPreflightHeaders = buildPreflightHeaders;
1783
+ exports.collectStreamEvents = collectStreamEvents;
1784
+ exports.createCompleteEvent = createCompleteEvent;
1785
+ exports.createDataEvent = createDataEvent;
1786
+ exports.createErrorEvent = createErrorEvent;
1787
+ exports.createHttpContext = createHttpContext;
1788
+ exports.createMessageEvent = createMessageEvent;
1789
+ exports.createNoopEvent = createNoopEvent;
1790
+ exports.createStreamContext = createStreamContext;
1791
+ exports.createTextEvent = createTextEvent;
1792
+ exports.createToolCallEvent = createToolCallEvent;
1793
+ exports.createToolResultEvent = createToolResultEvent;
1794
+ exports.defaultHttpTransform = defaultHttpTransform;
1795
+ exports.extractToken = extractToken;
1796
+ exports.fabricHttp = fabricHttp;
1797
+ exports.formatNdjsonEvent = formatNdjsonEvent;
1798
+ exports.formatSseEvent = formatSseEvent;
1799
+ exports.formatStreamEvent = formatStreamEvent;
1800
+ exports.getAllowedOrigin = getAllowedOrigin;
1801
+ exports.getAuthHeader = getAuthHeader;
1802
+ exports.getStreamContentType = getStreamContentType;
1803
+ exports.isAsyncIterable = isAsyncIterable;
1804
+ exports.isAuthorizationRequired = isAuthorizationRequired;
1805
+ exports.isFabricHttpServer = isFabricHttpServer;
1806
+ exports.isFabricHttpService = isFabricHttpService;
1807
+ exports.isPreflightRequest = isPreflightRequest;
1808
+ exports.isStreamingEnabled = isStreamingEnabled;
1809
+ exports.llmChunkToHttpEvent = llmChunkToHttpEvent;
1810
+ exports.normalizeCorsConfig = normalizeCorsConfig;
1811
+ exports.normalizeStreamConfig = normalizeStreamConfig;
1812
+ exports.parseBody = parseBody;
1813
+ exports.parsePathParams = parsePathParams;
1814
+ exports.parseQueryString = parseQueryString;
1815
+ exports.pipeLlmStream = pipeLlmStream;
1816
+ exports.pipeLlmStreamToWriter = pipeLlmStreamToWriter;
1817
+ exports.transformHttpToInput = transformHttpToInput;
1818
+ exports.validateAuthorization = validateAuthorization;
1819
+ exports.wrapServiceForStreaming = wrapServiceForStreaming;
1820
+ //# sourceMappingURL=index.cjs.map