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