@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,967 @@
1
+ 'use strict';
2
+
3
+ var aws = require('@jaypie/aws');
4
+ var lambda = require('@jaypie/lambda');
5
+ var errors = require('@jaypie/errors');
6
+
7
+ /**
8
+ * Meta-modeling Constants
9
+ */
10
+ // =============================================================================
11
+ // Constants
12
+ // =============================================================================
13
+ /** Root organizational unit */
14
+ /** Fabric version - used to identify pre-instantiated Services */
15
+ const FABRIC_VERSION = "0.1.0";
16
+
17
+ /**
18
+ * Date Type Conversion for @jaypie/fabric
19
+ *
20
+ * Adds Date as a supported type in the fabric type system.
21
+ * Follows the same conversion patterns as String, Number, Boolean.
22
+ */
23
+ /**
24
+ * Convert a value to a Date
25
+ *
26
+ * Supported inputs:
27
+ * - Date: returned as-is (validated)
28
+ * - Number: treated as Unix timestamp (milliseconds)
29
+ * - String: parsed via Date constructor (ISO 8601, etc.)
30
+ * - Object with value property: unwrapped and converted
31
+ *
32
+ * @throws BadRequestError if value cannot be converted to valid Date
33
+ */
34
+ function fabricDate(value) {
35
+ // Already a Date
36
+ if (value instanceof Date) {
37
+ if (Number.isNaN(value.getTime())) {
38
+ throw new errors.BadRequestError("Invalid Date value");
39
+ }
40
+ return value;
41
+ }
42
+ // Null/undefined
43
+ if (value === null || value === undefined) {
44
+ throw new errors.BadRequestError("Cannot convert null or undefined to Date");
45
+ }
46
+ // Object with value property (fabric pattern)
47
+ if (typeof value === "object" && value !== null && "value" in value) {
48
+ return fabricDate(value.value);
49
+ }
50
+ // Number (timestamp in milliseconds)
51
+ if (typeof value === "number") {
52
+ if (Number.isNaN(value)) {
53
+ throw new errors.BadRequestError("Cannot convert NaN to Date");
54
+ }
55
+ const date = new Date(value);
56
+ if (Number.isNaN(date.getTime())) {
57
+ throw new errors.BadRequestError(`Cannot convert ${value} to Date`);
58
+ }
59
+ return date;
60
+ }
61
+ // String (ISO 8601 or parseable format)
62
+ if (typeof value === "string") {
63
+ // Empty string is invalid
64
+ if (value.trim() === "") {
65
+ throw new errors.BadRequestError("Cannot convert empty string to Date");
66
+ }
67
+ const date = new Date(value);
68
+ if (Number.isNaN(date.getTime())) {
69
+ throw new errors.BadRequestError(`Cannot convert "${value}" to Date`);
70
+ }
71
+ return date;
72
+ }
73
+ // Boolean cannot be converted to Date
74
+ if (typeof value === "boolean") {
75
+ throw new errors.BadRequestError("Cannot convert boolean to Date");
76
+ }
77
+ // Arrays - attempt single element extraction
78
+ if (Array.isArray(value)) {
79
+ if (value.length === 1) {
80
+ return fabricDate(value[0]);
81
+ }
82
+ throw new errors.BadRequestError(`Cannot convert array with ${value.length} elements to Date`);
83
+ }
84
+ throw new errors.BadRequestError(`Cannot convert ${typeof value} to Date`);
85
+ }
86
+ /**
87
+ * Type guard for Date type in schema definitions
88
+ */
89
+ function isDateType(type) {
90
+ return type === Date;
91
+ }
92
+
93
+ // Fabric functions for @jaypie/fabric
94
+ /**
95
+ * Try to parse a string as JSON if it looks like JSON
96
+ * Returns the parsed value or the original string if not JSON
97
+ */
98
+ function tryParseJson(value) {
99
+ const trimmed = value.trim();
100
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
101
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
102
+ try {
103
+ return JSON.parse(trimmed);
104
+ }
105
+ catch {
106
+ // Not valid JSON, return original
107
+ return value;
108
+ }
109
+ }
110
+ return value;
111
+ }
112
+ /**
113
+ * Unwrap arrays and objects to get to the scalar value
114
+ * - Single-element arrays unwrap to their element
115
+ * - Objects with value property unwrap to that value
116
+ * - Recursively unwraps nested structures
117
+ */
118
+ function unwrapToScalar(value) {
119
+ if (value === undefined || value === null) {
120
+ return value;
121
+ }
122
+ // Unwrap single-element arrays
123
+ if (Array.isArray(value)) {
124
+ if (value.length === 0) {
125
+ return undefined;
126
+ }
127
+ if (value.length === 1) {
128
+ return unwrapToScalar(value[0]);
129
+ }
130
+ throw new errors.BadRequestError("Cannot convert multi-value array to scalar");
131
+ }
132
+ // Unwrap objects with value property
133
+ if (typeof value === "object") {
134
+ const obj = value;
135
+ if ("value" in obj) {
136
+ return unwrapToScalar(obj.value);
137
+ }
138
+ throw new errors.BadRequestError("Object must have a value attribute");
139
+ }
140
+ return value;
141
+ }
142
+ /**
143
+ * Prepare a value for scalar conversion by parsing JSON strings and unwrapping
144
+ */
145
+ function prepareForScalarConversion(value) {
146
+ if (value === undefined || value === null) {
147
+ return value;
148
+ }
149
+ // Try to parse JSON strings
150
+ if (typeof value === "string") {
151
+ const parsed = tryParseJson(value);
152
+ if (parsed !== value) {
153
+ // Successfully parsed, unwrap the result
154
+ return unwrapToScalar(parsed);
155
+ }
156
+ return value;
157
+ }
158
+ // Unwrap arrays and objects
159
+ if (Array.isArray(value) || typeof value === "object") {
160
+ return unwrapToScalar(value);
161
+ }
162
+ return value;
163
+ }
164
+ /**
165
+ * Convert a value to a boolean
166
+ * - Arrays, objects, and JSON strings are unwrapped first
167
+ * - String "true" becomes true
168
+ * - String "false" becomes false
169
+ * - Strings that parse to numbers: positive = true, zero or negative = false
170
+ * - Numbers: positive = true, zero or negative = false
171
+ * - Boolean passes through
172
+ */
173
+ function fabricBoolean(value) {
174
+ // Prepare value by parsing JSON and unwrapping arrays/objects
175
+ const prepared = prepareForScalarConversion(value);
176
+ if (prepared === undefined || prepared === null) {
177
+ return undefined;
178
+ }
179
+ if (typeof prepared === "boolean") {
180
+ return prepared;
181
+ }
182
+ if (typeof prepared === "string") {
183
+ if (prepared === "") {
184
+ return undefined;
185
+ }
186
+ const lower = prepared.toLowerCase();
187
+ if (lower === "true") {
188
+ return true;
189
+ }
190
+ if (lower === "false") {
191
+ return false;
192
+ }
193
+ // Try to parse as number
194
+ const num = parseFloat(prepared);
195
+ if (isNaN(num)) {
196
+ throw new errors.BadRequestError(`Cannot convert "${prepared}" to Boolean`);
197
+ }
198
+ return num > 0;
199
+ }
200
+ if (typeof prepared === "number") {
201
+ if (isNaN(prepared)) {
202
+ throw new errors.BadRequestError("Cannot convert NaN to Boolean");
203
+ }
204
+ return prepared > 0;
205
+ }
206
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to Boolean`);
207
+ }
208
+ /**
209
+ * Convert a value to a number
210
+ * - Arrays, objects, and JSON strings are unwrapped first
211
+ * - String "" becomes undefined
212
+ * - String "true" becomes 1
213
+ * - String "false" becomes 0
214
+ * - Strings that parse to numbers use those values
215
+ * - Strings that parse to NaN throw BadRequestError
216
+ * - Boolean true becomes 1, false becomes 0
217
+ * - Number passes through
218
+ */
219
+ function fabricNumber(value) {
220
+ // Prepare value by parsing JSON and unwrapping arrays/objects
221
+ const prepared = prepareForScalarConversion(value);
222
+ if (prepared === undefined || prepared === null) {
223
+ return undefined;
224
+ }
225
+ if (typeof prepared === "number") {
226
+ if (isNaN(prepared)) {
227
+ throw new errors.BadRequestError("Cannot convert NaN to Number");
228
+ }
229
+ return prepared;
230
+ }
231
+ if (typeof prepared === "boolean") {
232
+ return prepared ? 1 : 0;
233
+ }
234
+ if (typeof prepared === "string") {
235
+ if (prepared === "") {
236
+ return undefined;
237
+ }
238
+ const lower = prepared.toLowerCase();
239
+ if (lower === "true") {
240
+ return 1;
241
+ }
242
+ if (lower === "false") {
243
+ return 0;
244
+ }
245
+ const num = parseFloat(prepared);
246
+ if (isNaN(num)) {
247
+ throw new errors.BadRequestError(`Cannot convert "${prepared}" to Number`);
248
+ }
249
+ return num;
250
+ }
251
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to Number`);
252
+ }
253
+ /**
254
+ * Convert a value to a string
255
+ * - Arrays, objects, and JSON strings are unwrapped first
256
+ * - String "" becomes undefined
257
+ * - Boolean true becomes "true", false becomes "false"
258
+ * - Number converts to string representation
259
+ * - String passes through
260
+ */
261
+ function fabricString(value) {
262
+ // Prepare value by parsing JSON and unwrapping arrays/objects
263
+ const prepared = prepareForScalarConversion(value);
264
+ if (prepared === undefined || prepared === null) {
265
+ return undefined;
266
+ }
267
+ if (typeof prepared === "string") {
268
+ if (prepared === "") {
269
+ return undefined;
270
+ }
271
+ return prepared;
272
+ }
273
+ if (typeof prepared === "boolean") {
274
+ return prepared ? "true" : "false";
275
+ }
276
+ if (typeof prepared === "number") {
277
+ if (isNaN(prepared)) {
278
+ throw new errors.BadRequestError("Cannot convert NaN to String");
279
+ }
280
+ return String(prepared);
281
+ }
282
+ throw new errors.BadRequestError(`Cannot convert ${typeof prepared} to String`);
283
+ }
284
+ /**
285
+ * Convert a value to an array
286
+ * - Non-arrays become arrays containing that value
287
+ * - Arrays of a single value become that value (unwrapped)
288
+ * - Multi-value arrays throw BadRequestError
289
+ * - undefined/null become undefined
290
+ */
291
+ function fabricArray(value) {
292
+ if (value === undefined || value === null) {
293
+ return undefined;
294
+ }
295
+ if (Array.isArray(value)) {
296
+ // Arrays pass through (single-element unwrapping happens when converting FROM array)
297
+ return value;
298
+ }
299
+ // Non-arrays become single-element arrays
300
+ return [value];
301
+ }
302
+ /**
303
+ * Convert a value to an object with a value property
304
+ * - Scalars become { value: scalar }
305
+ * - Arrays become { value: array }
306
+ * - Objects with a value attribute pass through
307
+ * - Objects without a value attribute throw BadRequestError
308
+ * - undefined/null become undefined
309
+ */
310
+ function fabricObject(value) {
311
+ if (value === undefined || value === null) {
312
+ return undefined;
313
+ }
314
+ // Check if already an object (but not an array)
315
+ if (typeof value === "object" && !Array.isArray(value)) {
316
+ const obj = value;
317
+ if ("value" in obj) {
318
+ return obj;
319
+ }
320
+ throw new errors.BadRequestError("Object must have a value attribute");
321
+ }
322
+ // Scalars and arrays become { value: ... }
323
+ return { value };
324
+ }
325
+ /**
326
+ * Check if a type is a typed array (e.g., [String], [Number], [], etc.)
327
+ */
328
+ function isTypedArrayType(type) {
329
+ return Array.isArray(type);
330
+ }
331
+ /**
332
+ * Split a string on comma or tab delimiters for typed array conversion.
333
+ * Only splits if the string contains commas or tabs.
334
+ * Returns the original value if not a string or no delimiters found.
335
+ */
336
+ function splitStringForArray(value) {
337
+ if (typeof value !== "string") {
338
+ return value;
339
+ }
340
+ // Check for comma or tab delimiters
341
+ if (value.includes(",")) {
342
+ return value.split(",").map((s) => s.trim());
343
+ }
344
+ if (value.includes("\t")) {
345
+ return value.split("\t").map((s) => s.trim());
346
+ }
347
+ return value;
348
+ }
349
+ /**
350
+ * Try to parse a string as JSON for array context.
351
+ * Returns parsed value if it's an array, otherwise returns original.
352
+ */
353
+ function tryParseJsonArray(value) {
354
+ if (typeof value !== "string") {
355
+ return value;
356
+ }
357
+ const trimmed = value.trim();
358
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
359
+ try {
360
+ const parsed = JSON.parse(trimmed);
361
+ if (Array.isArray(parsed)) {
362
+ return parsed;
363
+ }
364
+ }
365
+ catch {
366
+ // Not valid JSON, fall through
367
+ }
368
+ }
369
+ return value;
370
+ }
371
+ /**
372
+ * Get the element type from a typed array type
373
+ * Returns undefined for untyped arrays ([])
374
+ */
375
+ function getArrayElementType(type) {
376
+ if (type.length === 0) {
377
+ return undefined; // Untyped array
378
+ }
379
+ const elementType = type[0];
380
+ // Handle constructor types
381
+ if (elementType === Boolean)
382
+ return "boolean";
383
+ if (elementType === Number)
384
+ return "number";
385
+ if (elementType === String)
386
+ return "string";
387
+ if (elementType === Object)
388
+ return "object";
389
+ // Handle string types
390
+ if (elementType === "boolean")
391
+ return "boolean";
392
+ if (elementType === "number")
393
+ return "number";
394
+ if (elementType === "string")
395
+ return "string";
396
+ if (elementType === "object")
397
+ return "object";
398
+ // Handle shorthand types
399
+ if (elementType === "")
400
+ return "string"; // "" shorthand for String
401
+ if (typeof elementType === "object" &&
402
+ elementType !== null &&
403
+ Object.keys(elementType).length === 0) {
404
+ return "object"; // {} shorthand for Object
405
+ }
406
+ throw new errors.BadRequestError(`Unknown array element type: ${String(elementType)}`);
407
+ }
408
+ /**
409
+ * Convert a value to a typed array
410
+ * - Tries to parse JSON arrays first
411
+ * - Splits strings on comma/tab if present
412
+ * - Wraps non-arrays in an array
413
+ * - Converts each element to the specified element type
414
+ */
415
+ function fabricTypedArray(value, elementType) {
416
+ // Try to parse JSON array first
417
+ let processed = tryParseJsonArray(value);
418
+ // If still a string, try to split on comma/tab
419
+ processed = splitStringForArray(processed);
420
+ // Convert to array (wraps non-arrays)
421
+ const array = fabricArray(processed);
422
+ if (array === undefined) {
423
+ return undefined;
424
+ }
425
+ // If no element type specified, return as-is
426
+ if (elementType === undefined) {
427
+ return array;
428
+ }
429
+ // Convert each element to the element type
430
+ return array.map((element, index) => {
431
+ try {
432
+ switch (elementType) {
433
+ case "boolean":
434
+ return fabricBoolean(element);
435
+ case "number":
436
+ return fabricNumber(element);
437
+ case "object":
438
+ return fabricObject(element);
439
+ case "string":
440
+ return fabricString(element);
441
+ default:
442
+ throw new errors.BadRequestError(`Unknown element type: ${elementType}`);
443
+ }
444
+ }
445
+ catch (error) {
446
+ if (error instanceof errors.BadRequestError) {
447
+ throw new errors.BadRequestError(`Cannot convert array element at index ${index}: ${error.message}`);
448
+ }
449
+ throw error;
450
+ }
451
+ });
452
+ }
453
+ /**
454
+ * Fabric a value to the specified type
455
+ */
456
+ function fabric(value, type) {
457
+ // Check for Date type first
458
+ if (isDateType(type)) {
459
+ return fabricDate(value);
460
+ }
461
+ // Check for typed array types
462
+ if (isTypedArrayType(type)) {
463
+ const elementType = getArrayElementType(type);
464
+ return fabricTypedArray(value, elementType);
465
+ }
466
+ const normalizedType = normalizeType(type);
467
+ switch (normalizedType) {
468
+ case "array":
469
+ return fabricArray(value);
470
+ case "boolean":
471
+ return fabricBoolean(value);
472
+ case "number":
473
+ return fabricNumber(value);
474
+ case "object":
475
+ return fabricObject(value);
476
+ case "string":
477
+ return fabricString(value);
478
+ default:
479
+ throw new errors.BadRequestError(`Unknown type: ${String(type)}`);
480
+ }
481
+ }
482
+ /**
483
+ * Normalize type to string representation
484
+ */
485
+ function normalizeType(type) {
486
+ if (type === Array || type === "array") {
487
+ return "array";
488
+ }
489
+ if (type === Boolean || type === "boolean") {
490
+ return "boolean";
491
+ }
492
+ if (type === Number || type === "number") {
493
+ return "number";
494
+ }
495
+ if (type === Object || type === "object") {
496
+ return "object";
497
+ }
498
+ if (type === String || type === "string") {
499
+ return "string";
500
+ }
501
+ throw new errors.BadRequestError(`Unknown type: ${String(type)}`);
502
+ }
503
+
504
+ // Service for @jaypie/fabric
505
+ /**
506
+ * Check if a single-element array is a typed array type constructor.
507
+ */
508
+ function isTypedArrayConstructor(element) {
509
+ return (element === Boolean ||
510
+ element === Number ||
511
+ element === String ||
512
+ element === Object ||
513
+ element === "boolean" ||
514
+ element === "number" ||
515
+ element === "string" ||
516
+ element === "object" ||
517
+ element === "" ||
518
+ (typeof element === "object" &&
519
+ element !== null &&
520
+ !(element instanceof RegExp) &&
521
+ Object.keys(element).length === 0));
522
+ }
523
+ /**
524
+ * Check if a type is a validated string type (array of string literals and/or RegExp).
525
+ * Distinguishes from typed arrays like [String], [Number], etc.
526
+ */
527
+ function isValidatedStringType(type) {
528
+ if (!Array.isArray(type)) {
529
+ return false;
530
+ }
531
+ // Empty array is untyped array, not validated string
532
+ if (type.length === 0) {
533
+ return false;
534
+ }
535
+ // Single-element arrays with type constructors are typed arrays
536
+ if (type.length === 1 && isTypedArrayConstructor(type[0])) {
537
+ return false;
538
+ }
539
+ // Check that all elements are strings or RegExp
540
+ return type.every((item) => typeof item === "string" || item instanceof RegExp);
541
+ }
542
+ /**
543
+ * Check if a type is a validated number type (array of number literals).
544
+ * Distinguishes from typed arrays like [Number], etc.
545
+ */
546
+ function isValidatedNumberType(type) {
547
+ if (!Array.isArray(type)) {
548
+ return false;
549
+ }
550
+ // Empty array is untyped array, not validated number
551
+ if (type.length === 0) {
552
+ return false;
553
+ }
554
+ // Single-element arrays with type constructors are typed arrays
555
+ if (type.length === 1 && isTypedArrayConstructor(type[0])) {
556
+ return false;
557
+ }
558
+ // Check that all elements are numbers
559
+ return type.every((item) => typeof item === "number");
560
+ }
561
+ /**
562
+ * Parse input string as JSON if it's a string
563
+ */
564
+ function parseInput(input) {
565
+ if (input === undefined || input === null) {
566
+ return {};
567
+ }
568
+ if (typeof input === "string") {
569
+ if (input === "") {
570
+ return {};
571
+ }
572
+ try {
573
+ const parsed = JSON.parse(input);
574
+ if (typeof parsed !== "object" ||
575
+ parsed === null ||
576
+ Array.isArray(parsed)) {
577
+ throw new errors.BadRequestError("Input must be an object");
578
+ }
579
+ return parsed;
580
+ }
581
+ catch (error) {
582
+ if (error instanceof errors.BadRequestError) {
583
+ throw error;
584
+ }
585
+ throw new errors.BadRequestError("Invalid JSON input");
586
+ }
587
+ }
588
+ if (typeof input === "object" && !Array.isArray(input)) {
589
+ return input;
590
+ }
591
+ throw new errors.BadRequestError("Input must be an object or JSON string");
592
+ }
593
+ /**
594
+ * Run validation on a value (supports async validators)
595
+ */
596
+ async function runValidation(value, validate, fieldName) {
597
+ if (typeof validate === "function") {
598
+ const result = await validate(value);
599
+ if (result === false) {
600
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
601
+ }
602
+ }
603
+ else if (validate instanceof RegExp) {
604
+ if (typeof value !== "string" || !validate.test(value)) {
605
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
606
+ }
607
+ }
608
+ else if (Array.isArray(validate)) {
609
+ // Check if value matches any item in the array
610
+ for (const item of validate) {
611
+ if (item instanceof RegExp) {
612
+ if (typeof value === "string" && item.test(value)) {
613
+ return; // Match found
614
+ }
615
+ }
616
+ else if (typeof item === "function") {
617
+ try {
618
+ const result = await item(value);
619
+ if (result !== false) {
620
+ return; // Match found
621
+ }
622
+ }
623
+ catch {
624
+ // Continue to next item
625
+ }
626
+ }
627
+ else if (value === item) {
628
+ return; // Scalar match found
629
+ }
630
+ }
631
+ throw new errors.BadRequestError(`Validation failed for field "${fieldName}"`);
632
+ }
633
+ }
634
+ /**
635
+ * Check if a field is required
636
+ * A field is required unless it has a default OR required is explicitly false
637
+ */
638
+ function isFieldRequired(definition) {
639
+ if (definition.required === false) {
640
+ return false;
641
+ }
642
+ if (definition.default !== undefined) {
643
+ return false;
644
+ }
645
+ return true;
646
+ }
647
+ /**
648
+ * Process a single field through conversion and validation
649
+ */
650
+ async function processField(fieldName, value, definition) {
651
+ // Apply default if value is undefined
652
+ let processedValue = value;
653
+ if (processedValue === undefined && definition.default !== undefined) {
654
+ processedValue = definition.default;
655
+ }
656
+ // Determine actual type and validation
657
+ let actualType = definition.type;
658
+ let validation = definition.validate;
659
+ // Handle bare RegExp shorthand: /regex/
660
+ if (definition.type instanceof RegExp) {
661
+ actualType = String;
662
+ validation = definition.type; // The RegExp becomes the validation
663
+ }
664
+ // Handle validated string shorthand: ["value1", "value2"] or [/regex/]
665
+ else if (isValidatedStringType(definition.type)) {
666
+ actualType = String;
667
+ validation = definition.type; // The array becomes the validation
668
+ }
669
+ // Handle validated number shorthand: [1, 2, 3]
670
+ else if (isValidatedNumberType(definition.type)) {
671
+ actualType = Number;
672
+ validation = definition.type; // The array becomes the validation
673
+ }
674
+ // Fabric to target type
675
+ const convertedValue = fabric(processedValue, actualType);
676
+ // Check if required field is missing
677
+ if (convertedValue === undefined && isFieldRequired(definition)) {
678
+ throw new errors.BadRequestError(`Missing required field "${fieldName}"`);
679
+ }
680
+ // Run validation if provided
681
+ if (validation !== undefined && convertedValue !== undefined) {
682
+ await runValidation(convertedValue, validation, fieldName);
683
+ }
684
+ return convertedValue;
685
+ }
686
+ /**
687
+ * Fabric a service function
688
+ *
689
+ * Service builds a function that initiates a "controller" step that:
690
+ * - Parses the input if it is a string to object
691
+ * - Fabrics each input field to its type
692
+ * - Calls the validation function or regular expression or checks the array
693
+ * - Calls the service function and returns the response
694
+ *
695
+ * The returned function has config properties for introspection.
696
+ */
697
+ function fabricService(config) {
698
+ const { input: inputDefinitions, service } = config;
699
+ const handler = async (rawInput, context) => {
700
+ // Parse input (handles string JSON)
701
+ const parsedInput = parseInput(rawInput);
702
+ // If no input definitions, pass through to service or return parsed input
703
+ if (!inputDefinitions) {
704
+ if (service) {
705
+ return service(parsedInput, context);
706
+ }
707
+ return parsedInput;
708
+ }
709
+ // Process all fields in parallel
710
+ const entries = Object.entries(inputDefinitions);
711
+ const processedValues = await Promise.all(entries.map(([fieldName, definition]) => processField(fieldName, parsedInput[fieldName], definition)));
712
+ // Build processed input object
713
+ const processedInput = {};
714
+ entries.forEach(([fieldName], index) => {
715
+ processedInput[fieldName] = processedValues[index];
716
+ });
717
+ // Return processed input if no service, otherwise call service
718
+ if (service) {
719
+ return service(processedInput, context);
720
+ }
721
+ return processedInput;
722
+ };
723
+ // Attach config properties directly to handler for flat access
724
+ const typedHandler = handler;
725
+ typedHandler.$fabric = FABRIC_VERSION;
726
+ if (config.alias !== undefined)
727
+ typedHandler.alias = config.alias;
728
+ if (config.description !== undefined)
729
+ typedHandler.description = config.description;
730
+ if (config.input !== undefined)
731
+ typedHandler.input = config.input;
732
+ if (config.service !== undefined)
733
+ typedHandler.service = config.service;
734
+ return typedHandler;
735
+ }
736
+
737
+ // Resolve inline service definitions to full Service objects
738
+ /**
739
+ * Type guard to check if a value is a pre-instantiated Service
740
+ * A Service is a function with the `$fabric` property set by fabricService
741
+ */
742
+ function isService$1(value) {
743
+ return typeof value === "function" && "$fabric" in value;
744
+ }
745
+ /**
746
+ * Resolve a service configuration to a full Service object
747
+ *
748
+ * Supports two patterns:
749
+ * 1. Inline service definition - pass a plain function as `service` along with
750
+ * `alias`, `description`, and `input` in the config
751
+ * 2. Pre-instantiated Service - pass a Service object as `service`
752
+ *
753
+ * When a pre-instantiated Service is passed, config fields act as overrides:
754
+ * - `alias` overrides service.alias
755
+ * - `description` overrides service.description
756
+ * - `input` overrides service.input
757
+ *
758
+ * The original Service is never mutated - a new Service is created when overrides
759
+ * are applied.
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * // Inline service definition
764
+ * const service = resolveService({
765
+ * alias: "greet",
766
+ * description: "Greet a user",
767
+ * input: { name: { type: String } },
768
+ * service: ({ name }) => `Hello, ${name}!`,
769
+ * });
770
+ *
771
+ * // Pre-instantiated with override
772
+ * const baseService = fabricService({ alias: "foo", service: (x) => x });
773
+ * const overridden = resolveService({
774
+ * alias: "bar", // Override alias
775
+ * service: baseService,
776
+ * });
777
+ * ```
778
+ */
779
+ function resolveService(config) {
780
+ const { alias, description, input, service } = config;
781
+ if (isService$1(service)) {
782
+ // Service is pre-instantiated - config fields act as overrides
783
+ // Create new Service with merged properties (config overrides service)
784
+ return fabricService({
785
+ alias: alias ?? service.alias,
786
+ description: description ?? service.description,
787
+ input: input ?? service.input,
788
+ service: service.service,
789
+ });
790
+ }
791
+ // Service is an inline function - create Service from config
792
+ return fabricService({
793
+ alias,
794
+ description,
795
+ input,
796
+ service,
797
+ });
798
+ }
799
+
800
+ // Lambda Service adapter for @jaypie/fabric
801
+ /**
802
+ * Type guard to check if a value is a pre-instantiated Service
803
+ * A Service is a function with the `$fabric` property set by fabricService
804
+ */
805
+ function isService(value) {
806
+ return typeof value === "function" && "$fabric" in value;
807
+ }
808
+ /**
809
+ * Type guard to check if a value is a config object (has service property)
810
+ */
811
+ function isConfig(value) {
812
+ return (typeof value === "object" &&
813
+ value !== null &&
814
+ "service" in value &&
815
+ typeof value.service === "function");
816
+ }
817
+ /**
818
+ * Fabric a Lambda handler that wraps a service
819
+ *
820
+ * This function creates a Lambda-compatible handler that:
821
+ * - Uses getMessages() to extract messages from SQS/SNS events
822
+ * - Calls the service once for each message
823
+ * - Returns the single response if one message, or an array of responses if multiple
824
+ * - Integrates with lambdaHandler for lifecycle management (secrets, setup, teardown, etc.)
825
+ *
826
+ * @param serviceOrConfig - The service function or configuration object
827
+ * @param options - Lambda handler options (secrets, setup, teardown, etc.)
828
+ * @returns A Lambda handler function
829
+ *
830
+ * @example
831
+ * ```typescript
832
+ * import { fabricLambda } from "@jaypie/fabric/lambda";
833
+ * import { myService } from "./services";
834
+ *
835
+ * // Direct service style
836
+ * export const handler = fabricLambda(myService);
837
+ *
838
+ * // Config object style
839
+ * export const handler2 = fabricLambda({
840
+ * service: myService,
841
+ * secrets: ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"],
842
+ * });
843
+ *
844
+ * // Service with options style
845
+ * export const handler3 = fabricLambda(myService, {
846
+ * secrets: ["ANTHROPIC_API_KEY"],
847
+ * });
848
+ * ```
849
+ */
850
+ function fabricLambda(serviceOrConfig, options = {}) {
851
+ // Normalize arguments and resolve service
852
+ let service;
853
+ let opts;
854
+ if (isConfig(serviceOrConfig)) {
855
+ // Config object with service property
856
+ const { alias, description, input, service: configService, ...configOpts } = serviceOrConfig;
857
+ // Resolve inline service or apply overrides
858
+ service = resolveService({
859
+ alias,
860
+ description,
861
+ input,
862
+ service: configService,
863
+ });
864
+ opts = configOpts;
865
+ }
866
+ else if (isService(serviceOrConfig)) {
867
+ // Pre-instantiated Service passed directly
868
+ service = serviceOrConfig;
869
+ opts = options;
870
+ }
871
+ else {
872
+ // Plain function passed directly - wrap it
873
+ service = resolveService({ service: serviceOrConfig });
874
+ opts = options;
875
+ }
876
+ // Use service.alias as the name for logging (can be overridden via options.name)
877
+ const name = opts.name ?? service.alias;
878
+ // Create context callbacks that wrap the registration callbacks with error swallowing
879
+ // Callback failures should never halt service execution
880
+ const sendMessage = opts.onMessage
881
+ ? async (message) => {
882
+ try {
883
+ await opts.onMessage(message);
884
+ }
885
+ catch {
886
+ // Swallow errors - callback failures should not halt execution
887
+ }
888
+ }
889
+ : undefined;
890
+ const contextOnError = opts.onError
891
+ ? async (error) => {
892
+ try {
893
+ await opts.onError(error);
894
+ }
895
+ catch {
896
+ // Swallow errors - callback failures should not halt execution
897
+ }
898
+ }
899
+ : undefined;
900
+ const contextOnFatal = opts.onFatal
901
+ ? async (error) => {
902
+ try {
903
+ await opts.onFatal(error);
904
+ }
905
+ catch {
906
+ // Swallow errors - callback failures should not halt execution
907
+ }
908
+ }
909
+ : undefined;
910
+ // Create context for the service
911
+ const context = {
912
+ onError: contextOnError,
913
+ onFatal: contextOnFatal,
914
+ sendMessage,
915
+ };
916
+ // Create the inner Lambda handler logic (context param required for lambdaHandler signature)
917
+ const innerHandler = async (event,
918
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
919
+ _context) => {
920
+ // Extract messages from SQS/SNS event wrapper
921
+ const messages = aws.getMessages(event);
922
+ // Process each message through the service
923
+ const results = [];
924
+ for (const message of messages) {
925
+ try {
926
+ const result = await service(message, context);
927
+ results.push(result);
928
+ }
929
+ catch (error) {
930
+ // Any thrown error is fatal - call onFatal or onError as fallback
931
+ if (opts.onFatal) {
932
+ await opts.onFatal(error);
933
+ }
934
+ else if (opts.onError) {
935
+ await opts.onError(error);
936
+ }
937
+ // Re-throw to let lambdaHandler handle it
938
+ throw error;
939
+ }
940
+ }
941
+ // Call onComplete if provided
942
+ const response = results.length === 1 ? results[0] : results;
943
+ if (opts.onComplete) {
944
+ try {
945
+ await opts.onComplete(response);
946
+ }
947
+ catch {
948
+ // Swallow errors - callback failures should not halt execution
949
+ }
950
+ }
951
+ return response;
952
+ };
953
+ // Wrap with lambdaHandler for lifecycle management
954
+ return lambda.lambdaHandler(innerHandler, {
955
+ chaos: opts.chaos,
956
+ name,
957
+ secrets: opts.secrets,
958
+ setup: opts.setup,
959
+ teardown: opts.teardown,
960
+ throw: opts.throw,
961
+ unavailable: opts.unavailable,
962
+ validate: opts.validate,
963
+ });
964
+ }
965
+
966
+ exports.fabricLambda = fabricLambda;
967
+ //# sourceMappingURL=index.cjs.map