@stone-js/aws-lambda-http-adapter 0.3.0 → 0.3.1

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 (3) hide show
  1. package/dist/browser.js +13 -13
  2. package/dist/index.js +341 -341
  3. package/package.json +5 -5
package/dist/browser.js CHANGED
@@ -19,19 +19,6 @@ class AwsLambdaHttpAdapterError extends IntegrationError {
19
19
  }
20
20
  }
21
21
 
22
- /**
23
- * Default blueprint configuration for the AWS Lambda Http Adapter.
24
- *
25
- * This blueprint defines the initial configuration for the AWS Lambda Http adapter
26
- * within the Stone.js framework. It includes:
27
- * - An alias for the AWS Lambda platform (`AWS_LAMBDA_HTTP_PLATFORM`).
28
- * - A default resolver function (currently a placeholder).
29
- * - Middleware, hooks, and state flags (`current`, `default`, `preferred`).
30
- *
31
- * NB: This is a stub for browser environments and does not perform any actual functionality in the browser.
32
- */
33
- const awsLambdaHttpAdapterBlueprint = { stone: { http: {}, adapters: [] } };
34
-
35
22
  /**
36
23
  * A Stone.js decorator that integrates the AWS Lambda HTTP Adapter with a class.
37
24
  *
@@ -65,4 +52,17 @@ const AwsLambdaHttp = (_options = {}) => {
65
52
  return classDecoratorLegacyWrapper((_target, _context) => { });
66
53
  };
67
54
 
55
+ /**
56
+ * Default blueprint configuration for the AWS Lambda Http Adapter.
57
+ *
58
+ * This blueprint defines the initial configuration for the AWS Lambda Http adapter
59
+ * within the Stone.js framework. It includes:
60
+ * - An alias for the AWS Lambda platform (`AWS_LAMBDA_HTTP_PLATFORM`).
61
+ * - A default resolver function (currently a placeholder).
62
+ * - Middleware, hooks, and state flags (`current`, `default`, `preferred`).
63
+ *
64
+ * NB: This is a stub for browser environments and does not perform any actual functionality in the browser.
65
+ */
66
+ const awsLambdaHttpAdapterBlueprint = { stone: { http: {}, adapters: [] } };
67
+
68
68
  export { AWS_LAMBDA_HTTP_PLATFORM, AwsLambdaHttp, AwsLambdaHttpAdapterError, awsLambdaHttpAdapterBlueprint };
package/dist/index.js CHANGED
@@ -1,13 +1,47 @@
1
- import { IntegrationError, Adapter, AdapterEventBuilder, defaultLoggerResolver, defaultKernelResolver, isNotEmpty, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
2
- import { IncomingHttpEvent, HTTP_INTERNAL_SERVER_ERROR, BinaryFileResponse, OutgoingHttpResponse, CookieCollection, isIpTrusted, getHostname, getProtocol, httpCoreBlueprint, isMultipart, getCharset, getFilesUploads } from '@stone-js/http-core';
1
+ import { defaultLoggerResolver, IntegrationError, Adapter, AdapterEventBuilder, isNotEmpty, defaultKernelResolver, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
3
2
  import mime from 'mime';
4
3
  import accepts from 'accepts';
5
4
  import statuses from 'statuses';
6
- import { getString } from '@stone-js/env';
5
+ import { HTTP_INTERNAL_SERVER_ERROR, IncomingHttpEvent, BinaryFileResponse, OutgoingHttpResponse, isMultipart, getFilesUploads, CookieCollection, isIpTrusted, getHostname, getProtocol, getCharset, httpCoreBlueprint } from '@stone-js/http-core';
7
6
  import { File } from '@stone-js/filesystem';
8
7
  import proxyAddr from 'proxy-addr';
9
8
  import bytes from 'bytes';
10
9
  import typeIs from 'type-is';
10
+ import { getString } from '@stone-js/env';
11
+
12
+ /**
13
+ * Class representing an AwsLambdaHttpErrorHandler.
14
+ */
15
+ class AwsLambdaHttpErrorHandler {
16
+ logger;
17
+ /**
18
+ * Create an NodeHttpErrorHandler.
19
+ *
20
+ * @param options - NodeHttpErrorHandler options.
21
+ */
22
+ constructor({ blueprint }) {
23
+ this.logger = blueprint.get('stone.logger.resolver', defaultLoggerResolver)(blueprint);
24
+ }
25
+ /**
26
+ * Handle an error.
27
+ *
28
+ * @param error - The error to handle.
29
+ * @param context - The context of the adapter.
30
+ * @returns The raw response builder.
31
+ */
32
+ handle(error, context) {
33
+ this.logger.error(error.message, { error });
34
+ const statusCode = error.cause?.status ?? HTTP_INTERNAL_SERVER_ERROR;
35
+ const type = accepts(context.rawEvent).type(['json', 'html']);
36
+ const contentType = mime.getType(type !== false ? type : 'txt') ?? context.rawEvent.headers['content-type'] ?? 'text/plain';
37
+ const headers = new Headers({ 'Content-Type': contentType });
38
+ return context
39
+ .rawResponseBuilder
40
+ .add('headers', headers)
41
+ .add('statusCode', statusCode)
42
+ .add('statusMessage', statuses.message[statusCode]);
43
+ }
44
+ }
11
45
 
12
46
  /**
13
47
  * Wrapper for HTTP raw responses in AWS Lambda.
@@ -221,40 +255,6 @@ class AwsLambdaHttpAdapter extends Adapter {
221
255
  }
222
256
  }
223
257
 
224
- /**
225
- * Class representing an AwsLambdaHttpErrorHandler.
226
- */
227
- class AwsLambdaHttpErrorHandler {
228
- logger;
229
- /**
230
- * Create an NodeHttpErrorHandler.
231
- *
232
- * @param options - NodeHttpErrorHandler options.
233
- */
234
- constructor({ blueprint }) {
235
- this.logger = blueprint.get('stone.logger.resolver', defaultLoggerResolver)(blueprint);
236
- }
237
- /**
238
- * Handle an error.
239
- *
240
- * @param error - The error to handle.
241
- * @param context - The context of the adapter.
242
- * @returns The raw response builder.
243
- */
244
- handle(error, context) {
245
- this.logger.error(error.message, { error });
246
- const statusCode = error.cause?.status ?? HTTP_INTERNAL_SERVER_ERROR;
247
- const type = accepts(context.rawEvent).type(['json', 'html']);
248
- const contentType = mime.getType(type !== false ? type : 'txt') ?? context.rawEvent.headers['content-type'] ?? 'text/plain';
249
- const headers = new Headers({ 'Content-Type': contentType });
250
- return context
251
- .rawResponseBuilder
252
- .add('headers', headers)
253
- .add('statusCode', statusCode)
254
- .add('statusMessage', statuses.message[statusCode]);
255
- }
256
- }
257
-
258
258
  /**
259
259
  * A constant representing the AWS Lambda HTTP platform identifier.
260
260
  *
@@ -276,154 +276,6 @@ const awsLambdaHttpAdapterResolver = (blueprint) => {
276
276
  return AwsLambdaHttpAdapter.create(blueprint);
277
277
  };
278
278
 
279
- function getDefaultExportFromCjs (x) {
280
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
281
- }
282
-
283
- var cjs;
284
- var hasRequiredCjs;
285
-
286
- function requireCjs () {
287
- if (hasRequiredCjs) return cjs;
288
- hasRequiredCjs = 1;
289
-
290
- var isMergeableObject = function isMergeableObject(value) {
291
- return isNonNullObject(value)
292
- && !isSpecial(value)
293
- };
294
-
295
- function isNonNullObject(value) {
296
- return !!value && typeof value === 'object'
297
- }
298
-
299
- function isSpecial(value) {
300
- var stringValue = Object.prototype.toString.call(value);
301
-
302
- return stringValue === '[object RegExp]'
303
- || stringValue === '[object Date]'
304
- || isReactElement(value)
305
- }
306
-
307
- // see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
308
- var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
309
- var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
310
-
311
- function isReactElement(value) {
312
- return value.$$typeof === REACT_ELEMENT_TYPE
313
- }
314
-
315
- function emptyTarget(val) {
316
- return Array.isArray(val) ? [] : {}
317
- }
318
-
319
- function cloneUnlessOtherwiseSpecified(value, options) {
320
- return (options.clone !== false && options.isMergeableObject(value))
321
- ? deepmerge(emptyTarget(value), value, options)
322
- : value
323
- }
324
-
325
- function defaultArrayMerge(target, source, options) {
326
- return target.concat(source).map(function(element) {
327
- return cloneUnlessOtherwiseSpecified(element, options)
328
- })
329
- }
330
-
331
- function getMergeFunction(key, options) {
332
- if (!options.customMerge) {
333
- return deepmerge
334
- }
335
- var customMerge = options.customMerge(key);
336
- return typeof customMerge === 'function' ? customMerge : deepmerge
337
- }
338
-
339
- function getEnumerableOwnPropertySymbols(target) {
340
- return Object.getOwnPropertySymbols
341
- ? Object.getOwnPropertySymbols(target).filter(function(symbol) {
342
- return Object.propertyIsEnumerable.call(target, symbol)
343
- })
344
- : []
345
- }
346
-
347
- function getKeys(target) {
348
- return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
349
- }
350
-
351
- function propertyIsOnObject(object, property) {
352
- try {
353
- return property in object
354
- } catch(_) {
355
- return false
356
- }
357
- }
358
-
359
- // Protects from prototype poisoning and unexpected merging up the prototype chain.
360
- function propertyIsUnsafe(target, key) {
361
- return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
362
- && !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
363
- && Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
364
- }
365
-
366
- function mergeObject(target, source, options) {
367
- var destination = {};
368
- if (options.isMergeableObject(target)) {
369
- getKeys(target).forEach(function(key) {
370
- destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
371
- });
372
- }
373
- getKeys(source).forEach(function(key) {
374
- if (propertyIsUnsafe(target, key)) {
375
- return
376
- }
377
-
378
- if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
379
- destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
380
- } else {
381
- destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
382
- }
383
- });
384
- return destination
385
- }
386
-
387
- function deepmerge(target, source, options) {
388
- options = options || {};
389
- options.arrayMerge = options.arrayMerge || defaultArrayMerge;
390
- options.isMergeableObject = options.isMergeableObject || isMergeableObject;
391
- // cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()
392
- // implementations can use it. The caller may not replace it.
393
- options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
394
-
395
- var sourceIsArray = Array.isArray(source);
396
- var targetIsArray = Array.isArray(target);
397
- var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
398
-
399
- if (!sourceAndTargetTypesMatch) {
400
- return cloneUnlessOtherwiseSpecified(source, options)
401
- } else if (sourceIsArray) {
402
- return options.arrayMerge(target, source, options)
403
- } else {
404
- return mergeObject(target, source, options)
405
- }
406
- }
407
-
408
- deepmerge.all = function deepmergeAll(array, options) {
409
- if (!Array.isArray(array)) {
410
- throw new Error('first argument should be an array')
411
- }
412
-
413
- return array.reduce(function(prev, next) {
414
- return deepmerge(prev, next, options)
415
- }, {})
416
- };
417
-
418
- var deepmerge_1 = deepmerge;
419
-
420
- cjs = deepmerge_1;
421
- return cjs;
422
- }
423
-
424
- var cjsExports = requireCjs();
425
- var deepmerge = /*@__PURE__*/getDefaultExportFromCjs(cjsExports);
426
-
427
279
  /**
428
280
  * Middleware to dynamically set response resolver for adapter.
429
281
  *
@@ -457,20 +309,19 @@ const metaAdapterBlueprintMiddleware = [
457
309
  ];
458
310
 
459
311
  /**
460
- * Middleware for handling incoming events and transforming them into Stone.js events.
312
+ * Class representing a FilesEventMiddleware.
461
313
  *
462
- * This class processes incoming HTTP requests, extracting relevant data such as URL, IP addresses,
463
- * headers, cookies, and more, and forwards them to the next middleware in the pipeline.
314
+ * @author Mr. Stone <evensstone@gmail.com>
464
315
  */
465
- class IncomingEventMiddleware {
316
+ class FilesEventMiddleware {
466
317
  /**
467
318
  * The blueprint for resolving configuration and dependencies.
468
319
  */
469
320
  blueprint;
470
321
  /**
471
- * Create an IncomingEventMiddleware instance.
322
+ * Create a FilesEventMiddleware.
472
323
  *
473
- * @param {blueprint} options - Options containing the blueprint for resolving configuration and dependencies.
324
+ * @param {blueprint} options - Options for creating the FilesEventMiddleware.
474
325
  */
475
326
  constructor({ blueprint }) {
476
327
  this.blueprint = blueprint;
@@ -480,26 +331,99 @@ class IncomingEventMiddleware {
480
331
  *
481
332
  * @param context - The adapter context containing the raw event, execution context, and other data.
482
333
  * @param next - The next middleware to be invoked in the pipeline.
483
- * @returns A promise that resolves to the processed context.
484
- * @throws {AwsLambdaHttpAdapterError} If required components are missing in the context.
334
+ * @returns A promise that resolves to the destination type after processing.
335
+ *
336
+ * @throws {AwsLambdaHttpAdapterError} If required components such as the rawEvent or IncomingEventBuilder are not provided.
485
337
  */
486
338
  async handle(context, next) {
487
- if ((context.rawEvent === undefined) || ((context.incomingEventBuilder?.add) === undefined)) {
339
+ if (context.rawEvent === undefined || context.incomingEventBuilder?.add === undefined) {
488
340
  throw new AwsLambdaHttpAdapterError('The context is missing required components.');
489
341
  }
490
- const proxyOptions = this.getProxyOptions();
491
- const cookieOptions = this.getCookieOptions();
492
- const url = this.extractUrl(context.rawEvent, proxyOptions);
493
- const ipAddresses = this.extractIpAddresses(context.rawEvent, proxyOptions);
494
- context
495
- .incomingEventBuilder
496
- .add('url', url)
497
- .add('ips', ipAddresses)
498
- .add('source', this.getSource(context))
499
- .add('headers', context.rawEvent.headers)
500
- // If not defined by other middleware
342
+ if (isMultipart(this.normalizeEvent(context.rawEvent))) {
343
+ const options = this.blueprint.get('stone.http.files.upload', {});
344
+ const response = await getFilesUploads(this.normalizeEvent(context.rawEvent), options);
345
+ const method = response.fields.$method$;
346
+ context
347
+ .incomingEventBuilder
348
+ .add('files', response.files)
349
+ .add('body', response.fields);
501
350
  // In fullstack forms, the method is spoofed and sent as a hidden field
502
- .addIf('method', this.getMethod(context.rawEvent))
351
+ isNotEmpty(method) && context.incomingEventBuilder.add('method', method);
352
+ }
353
+ return await next(context);
354
+ }
355
+ /**
356
+ * Normalize the incoming event to an IncomingMessage.
357
+ *
358
+ * @param rawEvent - The raw event to be normalized.
359
+ * @returns The normalized event.
360
+ */
361
+ normalizeEvent(rawEvent) {
362
+ let body = rawEvent.body;
363
+ if (typeof rawEvent.body === 'string') {
364
+ body = rawEvent.isBase64Encoded === true
365
+ ? Buffer.from(rawEvent.body, 'base64')
366
+ : Buffer.from(rawEvent.body, 'utf-8');
367
+ }
368
+ return {
369
+ body,
370
+ headers: {
371
+ 'content-type': rawEvent.headers['content-type'] ?? rawEvent.headers['Content-Type'],
372
+ 'content-length': rawEvent.headers['content-length'] ?? rawEvent.headers['Content-Length'],
373
+ 'transfer-encoding': rawEvent.headers['transfer-encoding'] ?? rawEvent.headers['Transfer-Encoding']
374
+ }
375
+ };
376
+ }
377
+ }
378
+ /**
379
+ * Meta Middleware for processing files uploads.
380
+ */
381
+ const MetaFilesEventMiddleware = { module: FilesEventMiddleware, isClass: true };
382
+
383
+ /**
384
+ * Middleware for handling incoming events and transforming them into Stone.js events.
385
+ *
386
+ * This class processes incoming HTTP requests, extracting relevant data such as URL, IP addresses,
387
+ * headers, cookies, and more, and forwards them to the next middleware in the pipeline.
388
+ */
389
+ class IncomingEventMiddleware {
390
+ /**
391
+ * The blueprint for resolving configuration and dependencies.
392
+ */
393
+ blueprint;
394
+ /**
395
+ * Create an IncomingEventMiddleware instance.
396
+ *
397
+ * @param {blueprint} options - Options containing the blueprint for resolving configuration and dependencies.
398
+ */
399
+ constructor({ blueprint }) {
400
+ this.blueprint = blueprint;
401
+ }
402
+ /**
403
+ * Handles the incoming event, processes it, and invokes the next middleware in the pipeline.
404
+ *
405
+ * @param context - The adapter context containing the raw event, execution context, and other data.
406
+ * @param next - The next middleware to be invoked in the pipeline.
407
+ * @returns A promise that resolves to the processed context.
408
+ * @throws {AwsLambdaHttpAdapterError} If required components are missing in the context.
409
+ */
410
+ async handle(context, next) {
411
+ if ((context.rawEvent === undefined) || ((context.incomingEventBuilder?.add) === undefined)) {
412
+ throw new AwsLambdaHttpAdapterError('The context is missing required components.');
413
+ }
414
+ const proxyOptions = this.getProxyOptions();
415
+ const cookieOptions = this.getCookieOptions();
416
+ const url = this.extractUrl(context.rawEvent, proxyOptions);
417
+ const ipAddresses = this.extractIpAddresses(context.rawEvent, proxyOptions);
418
+ context
419
+ .incomingEventBuilder
420
+ .add('url', url)
421
+ .add('ips', ipAddresses)
422
+ .add('source', this.getSource(context))
423
+ .add('headers', context.rawEvent.headers)
424
+ // If not defined by other middleware
425
+ // In fullstack forms, the method is spoofed and sent as a hidden field
426
+ .addIf('method', this.getMethod(context.rawEvent))
503
427
  .add('queryString', context.rawEvent.queryStringParameters)
504
428
  .add('protocol', this.getProtocol(context.rawEvent, proxyOptions))
505
429
  .add('cookies', CookieCollection.create(context.rawEvent.headers.cookie, cookieOptions, this.getCookieSecret()))
@@ -667,77 +591,6 @@ class ServerResponseMiddleware {
667
591
  */
668
592
  const MetaServerResponseMiddleware = { module: ServerResponseMiddleware, isClass: true };
669
593
 
670
- /**
671
- * Default blueprint configuration for the AWS Lambda Http Adapter.
672
- *
673
- * This blueprint defines the initial configuration for the AWS Lambda Http adapter
674
- * within the Stone.js framework. It includes:
675
- * - An alias for the AWS Lambda platform (`AWS_LAMBDA_HTTP_PLATFORM`).
676
- * - A default resolver function (currently a placeholder).
677
- * - Middleware, hooks, and state flags (`current`, `default`, `preferred`).
678
- */
679
- const awsLambdaHttpAdapterBlueprint = {
680
- stone: {
681
- ...httpCoreBlueprint.stone,
682
- blueprint: {
683
- middleware: metaAdapterBlueprintMiddleware
684
- },
685
- adapters: [
686
- {
687
- current: false,
688
- variant: 'server',
689
- platform: AWS_LAMBDA_HTTP_PLATFORM,
690
- middleware: [
691
- MetaIncomingEventMiddleware,
692
- MetaServerResponseMiddleware
693
- ],
694
- resolver: awsLambdaHttpAdapterResolver,
695
- eventHandlerResolver: defaultKernelResolver,
696
- errorHandlers: {
697
- default: { module: AwsLambdaHttpErrorHandler, isClass: true }
698
- },
699
- default: isNotEmpty(getString('AWS_LAMBDA_FUNCTION_NAME', ''))
700
- }
701
- ]
702
- }
703
- };
704
-
705
- /**
706
- * A Stone.js decorator that integrates the AWS Lambda HTTP Adapter with a class.
707
- *
708
- * This decorator modifies the class to seamlessly enable AWS Lambda HTTP as the
709
- * execution environment for a Stone.js application. By applying this decorator,
710
- * the class is automatically configured with the necessary blueprint for AWS Lambda HTTP.
711
- *
712
- * @template T - The type of the class being decorated. Defaults to `ClassType`.
713
- * @param options - Optional configuration to customize the AWS Lambda HTTP Adapter.
714
- *
715
- * @returns A class decorator that applies the AWS Lambda HTTP adapter configuration.
716
- *
717
- * @example
718
- * ```typescript
719
- * import { AwsLambdaHttp } from '@stone-js/aws-lambda-http-adapter';
720
- *
721
- * @AwsLambdaHttp({
722
- * alias: 'MyAwsLambdaHttpAdapter',
723
- * current: true,
724
- * })
725
- * class App {
726
- * // Your application logic here
727
- * }
728
- * ```
729
- */
730
- const AwsLambdaHttp = (options = {}) => {
731
- return classDecoratorLegacyWrapper((target, context) => {
732
- if (awsLambdaHttpAdapterBlueprint.stone?.adapters?.[0] !== undefined) {
733
- // Merge provided options with the default AWS Lambda HTTP adapter blueprint.
734
- awsLambdaHttpAdapterBlueprint.stone.adapters[0] = deepmerge(awsLambdaHttpAdapterBlueprint.stone.adapters[0], options);
735
- }
736
- // Add the modified blueprint to the target class.
737
- addBlueprint(target, context, awsLambdaHttpAdapterBlueprint);
738
- });
739
- };
740
-
741
594
  /**
742
595
  * Class representing a BodyEventMiddleware.
743
596
  *
@@ -872,76 +725,223 @@ class BodyEventMiddleware {
872
725
  */
873
726
  const MetaBodyEventMiddleware = { module: BodyEventMiddleware, isClass: true };
874
727
 
728
+ function getDefaultExportFromCjs (x) {
729
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
730
+ }
731
+
732
+ var cjs;
733
+ var hasRequiredCjs;
734
+
735
+ function requireCjs () {
736
+ if (hasRequiredCjs) return cjs;
737
+ hasRequiredCjs = 1;
738
+
739
+ var isMergeableObject = function isMergeableObject(value) {
740
+ return isNonNullObject(value)
741
+ && !isSpecial(value)
742
+ };
743
+
744
+ function isNonNullObject(value) {
745
+ return !!value && typeof value === 'object'
746
+ }
747
+
748
+ function isSpecial(value) {
749
+ var stringValue = Object.prototype.toString.call(value);
750
+
751
+ return stringValue === '[object RegExp]'
752
+ || stringValue === '[object Date]'
753
+ || isReactElement(value)
754
+ }
755
+
756
+ // see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
757
+ var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
758
+ var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
759
+
760
+ function isReactElement(value) {
761
+ return value.$$typeof === REACT_ELEMENT_TYPE
762
+ }
763
+
764
+ function emptyTarget(val) {
765
+ return Array.isArray(val) ? [] : {}
766
+ }
767
+
768
+ function cloneUnlessOtherwiseSpecified(value, options) {
769
+ return (options.clone !== false && options.isMergeableObject(value))
770
+ ? deepmerge(emptyTarget(value), value, options)
771
+ : value
772
+ }
773
+
774
+ function defaultArrayMerge(target, source, options) {
775
+ return target.concat(source).map(function(element) {
776
+ return cloneUnlessOtherwiseSpecified(element, options)
777
+ })
778
+ }
779
+
780
+ function getMergeFunction(key, options) {
781
+ if (!options.customMerge) {
782
+ return deepmerge
783
+ }
784
+ var customMerge = options.customMerge(key);
785
+ return typeof customMerge === 'function' ? customMerge : deepmerge
786
+ }
787
+
788
+ function getEnumerableOwnPropertySymbols(target) {
789
+ return Object.getOwnPropertySymbols
790
+ ? Object.getOwnPropertySymbols(target).filter(function(symbol) {
791
+ return Object.propertyIsEnumerable.call(target, symbol)
792
+ })
793
+ : []
794
+ }
795
+
796
+ function getKeys(target) {
797
+ return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
798
+ }
799
+
800
+ function propertyIsOnObject(object, property) {
801
+ try {
802
+ return property in object
803
+ } catch(_) {
804
+ return false
805
+ }
806
+ }
807
+
808
+ // Protects from prototype poisoning and unexpected merging up the prototype chain.
809
+ function propertyIsUnsafe(target, key) {
810
+ return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
811
+ && !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
812
+ && Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
813
+ }
814
+
815
+ function mergeObject(target, source, options) {
816
+ var destination = {};
817
+ if (options.isMergeableObject(target)) {
818
+ getKeys(target).forEach(function(key) {
819
+ destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
820
+ });
821
+ }
822
+ getKeys(source).forEach(function(key) {
823
+ if (propertyIsUnsafe(target, key)) {
824
+ return
825
+ }
826
+
827
+ if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
828
+ destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
829
+ } else {
830
+ destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
831
+ }
832
+ });
833
+ return destination
834
+ }
835
+
836
+ function deepmerge(target, source, options) {
837
+ options = options || {};
838
+ options.arrayMerge = options.arrayMerge || defaultArrayMerge;
839
+ options.isMergeableObject = options.isMergeableObject || isMergeableObject;
840
+ // cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()
841
+ // implementations can use it. The caller may not replace it.
842
+ options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
843
+
844
+ var sourceIsArray = Array.isArray(source);
845
+ var targetIsArray = Array.isArray(target);
846
+ var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
847
+
848
+ if (!sourceAndTargetTypesMatch) {
849
+ return cloneUnlessOtherwiseSpecified(source, options)
850
+ } else if (sourceIsArray) {
851
+ return options.arrayMerge(target, source, options)
852
+ } else {
853
+ return mergeObject(target, source, options)
854
+ }
855
+ }
856
+
857
+ deepmerge.all = function deepmergeAll(array, options) {
858
+ if (!Array.isArray(array)) {
859
+ throw new Error('first argument should be an array')
860
+ }
861
+
862
+ return array.reduce(function(prev, next) {
863
+ return deepmerge(prev, next, options)
864
+ }, {})
865
+ };
866
+
867
+ var deepmerge_1 = deepmerge;
868
+
869
+ cjs = deepmerge_1;
870
+ return cjs;
871
+ }
872
+
873
+ var cjsExports = requireCjs();
874
+ var deepmerge = /*@__PURE__*/getDefaultExportFromCjs(cjsExports);
875
+
875
876
  /**
876
- * Class representing a FilesEventMiddleware.
877
+ * Default blueprint configuration for the AWS Lambda Http Adapter.
877
878
  *
878
- * @author Mr. Stone <evensstone@gmail.com>
879
+ * This blueprint defines the initial configuration for the AWS Lambda Http adapter
880
+ * within the Stone.js framework. It includes:
881
+ * - An alias for the AWS Lambda platform (`AWS_LAMBDA_HTTP_PLATFORM`).
882
+ * - A default resolver function (currently a placeholder).
883
+ * - Middleware, hooks, and state flags (`current`, `default`, `preferred`).
879
884
  */
880
- class FilesEventMiddleware {
881
- /**
882
- * The blueprint for resolving configuration and dependencies.
883
- */
884
- blueprint;
885
- /**
886
- * Create a FilesEventMiddleware.
887
- *
888
- * @param {blueprint} options - Options for creating the FilesEventMiddleware.
889
- */
890
- constructor({ blueprint }) {
891
- this.blueprint = blueprint;
892
- }
893
- /**
894
- * Handles the incoming event, processes it, and invokes the next middleware in the pipeline.
895
- *
896
- * @param context - The adapter context containing the raw event, execution context, and other data.
897
- * @param next - The next middleware to be invoked in the pipeline.
898
- * @returns A promise that resolves to the destination type after processing.
899
- *
900
- * @throws {AwsLambdaHttpAdapterError} If required components such as the rawEvent or IncomingEventBuilder are not provided.
901
- */
902
- async handle(context, next) {
903
- if (context.rawEvent === undefined || context.incomingEventBuilder?.add === undefined) {
904
- throw new AwsLambdaHttpAdapterError('The context is missing required components.');
905
- }
906
- if (isMultipart(this.normalizeEvent(context.rawEvent))) {
907
- const options = this.blueprint.get('stone.http.files.upload', {});
908
- const response = await getFilesUploads(this.normalizeEvent(context.rawEvent), options);
909
- const method = response.fields.$method$;
910
- context
911
- .incomingEventBuilder
912
- .add('files', response.files)
913
- .add('body', response.fields);
914
- // In fullstack forms, the method is spoofed and sent as a hidden field
915
- isNotEmpty(method) && context.incomingEventBuilder.add('method', method);
916
- }
917
- return await next(context);
918
- }
919
- /**
920
- * Normalize the incoming event to an IncomingMessage.
921
- *
922
- * @param rawEvent - The raw event to be normalized.
923
- * @returns The normalized event.
924
- */
925
- normalizeEvent(rawEvent) {
926
- let body = rawEvent.body;
927
- if (typeof rawEvent.body === 'string') {
928
- body = rawEvent.isBase64Encoded === true
929
- ? Buffer.from(rawEvent.body, 'base64')
930
- : Buffer.from(rawEvent.body, 'utf-8');
931
- }
932
- return {
933
- body,
934
- headers: {
935
- 'content-type': rawEvent.headers['content-type'] ?? rawEvent.headers['Content-Type'],
936
- 'content-length': rawEvent.headers['content-length'] ?? rawEvent.headers['Content-Length'],
937
- 'transfer-encoding': rawEvent.headers['transfer-encoding'] ?? rawEvent.headers['Transfer-Encoding']
885
+ const awsLambdaHttpAdapterBlueprint = {
886
+ stone: {
887
+ ...httpCoreBlueprint.stone,
888
+ blueprint: {
889
+ middleware: metaAdapterBlueprintMiddleware
890
+ },
891
+ adapters: [
892
+ {
893
+ current: false,
894
+ variant: 'server',
895
+ platform: AWS_LAMBDA_HTTP_PLATFORM,
896
+ middleware: [
897
+ MetaIncomingEventMiddleware,
898
+ MetaServerResponseMiddleware
899
+ ],
900
+ resolver: awsLambdaHttpAdapterResolver,
901
+ eventHandlerResolver: defaultKernelResolver,
902
+ errorHandlers: {
903
+ default: { module: AwsLambdaHttpErrorHandler, isClass: true }
904
+ },
905
+ default: isNotEmpty(getString('AWS_LAMBDA_FUNCTION_NAME', ''))
938
906
  }
939
- };
907
+ ]
940
908
  }
941
- }
909
+ };
910
+
942
911
  /**
943
- * Meta Middleware for processing files uploads.
912
+ * A Stone.js decorator that integrates the AWS Lambda HTTP Adapter with a class.
913
+ *
914
+ * This decorator modifies the class to seamlessly enable AWS Lambda HTTP as the
915
+ * execution environment for a Stone.js application. By applying this decorator,
916
+ * the class is automatically configured with the necessary blueprint for AWS Lambda HTTP.
917
+ *
918
+ * @template T - The type of the class being decorated. Defaults to `ClassType`.
919
+ * @param options - Optional configuration to customize the AWS Lambda HTTP Adapter.
920
+ *
921
+ * @returns A class decorator that applies the AWS Lambda HTTP adapter configuration.
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * import { AwsLambdaHttp } from '@stone-js/aws-lambda-http-adapter';
926
+ *
927
+ * @AwsLambdaHttp({
928
+ * alias: 'MyAwsLambdaHttpAdapter',
929
+ * current: true,
930
+ * })
931
+ * class App {
932
+ * // Your application logic here
933
+ * }
934
+ * ```
944
935
  */
945
- const MetaFilesEventMiddleware = { module: FilesEventMiddleware, isClass: true };
936
+ const AwsLambdaHttp = (options = {}) => {
937
+ return classDecoratorLegacyWrapper((target, context) => {
938
+ if (awsLambdaHttpAdapterBlueprint.stone?.adapters?.[0] !== undefined) {
939
+ // Merge provided options with the default AWS Lambda HTTP adapter blueprint.
940
+ awsLambdaHttpAdapterBlueprint.stone.adapters[0] = deepmerge(awsLambdaHttpAdapterBlueprint.stone.adapters[0], options);
941
+ }
942
+ // Add the modified blueprint to the target class.
943
+ addBlueprint(target, context, awsLambdaHttpAdapterBlueprint);
944
+ });
945
+ };
946
946
 
947
947
  export { AWS_LAMBDA_HTTP_PLATFORM, AwsLambdaHttp, AwsLambdaHttpAdapter, AwsLambdaHttpAdapterError, AwsLambdaHttpErrorHandler, BodyEventMiddleware, FilesEventMiddleware, IncomingEventMiddleware, MetaBodyEventMiddleware, MetaFilesEventMiddleware, MetaIncomingEventMiddleware, MetaServerResponseMiddleware, RawHttpResponseWrapper, ServerResponseMiddleware, SetAwsLambdaHttpResponseResolverMiddleware, awsLambdaHttpAdapterBlueprint, awsLambdaHttpAdapterResolver, metaAdapterBlueprintMiddleware };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stone-js/aws-lambda-http-adapter",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Official AWS Lambda HTTP adapter for Stone.js. Run your Stone.js apps on AWS Lambda behind API Gateway with full Continuum lifecycle support.",
5
5
  "author": "Mr. Stone <evensstone@gmail.com>",
6
6
  "license": "MIT",
@@ -62,10 +62,10 @@
62
62
  "prepare": "husky"
63
63
  },
64
64
  "peerDependencies": {
65
- "@stone-js/core": "^0.1.1",
66
- "@stone-js/env": "^0.1.1",
67
- "@stone-js/filesystem": "^0.1.1",
68
- "@stone-js/http-core": "^0.1.2"
65
+ "@stone-js/core": "^0.2.1",
66
+ "@stone-js/env": "^0.1.2",
67
+ "@stone-js/filesystem": "^0.1.2",
68
+ "@stone-js/http-core": "^0.1.4"
69
69
  },
70
70
  "dependencies": {
71
71
  "accepts": "^1.3.8",