@ibgib/node-gib 0.0.9 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assumptions.respec.mjs.map +1 -1
- package/dist/email/aws/aws-sigv4-helper.d.mts +26 -0
- package/dist/email/aws/aws-sigv4-helper.d.mts.map +1 -0
- package/dist/email/aws/aws-sigv4-helper.mjs +94 -0
- package/dist/email/aws/aws-sigv4-helper.mjs.map +1 -0
- package/dist/email/aws/email-receiver-aws.d.mts +13 -0
- package/dist/email/aws/email-receiver-aws.d.mts.map +1 -0
- package/dist/email/aws/email-receiver-aws.mjs +97 -0
- package/dist/email/aws/email-receiver-aws.mjs.map +1 -0
- package/dist/email/aws/email-sender-aws.d.mts +23 -0
- package/dist/email/aws/email-sender-aws.d.mts.map +1 -0
- package/dist/email/aws/email-sender-aws.mjs +106 -0
- package/dist/email/aws/email-sender-aws.mjs.map +1 -0
- package/dist/email/aws/index.d.mts +7 -0
- package/dist/email/aws/index.d.mts.map +1 -0
- package/dist/email/aws/index.mjs +7 -0
- package/dist/email/aws/index.mjs.map +1 -0
- package/dist/email/email-constants.d.mts +15 -0
- package/dist/email/email-constants.d.mts.map +1 -0
- package/dist/email/email-constants.mjs +15 -0
- package/dist/email/email-constants.mjs.map +1 -0
- package/dist/email/email-helpers.d.mts +35 -0
- package/dist/email/email-helpers.d.mts.map +1 -0
- package/dist/email/email-helpers.mjs +144 -0
- package/dist/email/email-helpers.mjs.map +1 -0
- package/dist/email/email-receiver.d.mts +12 -0
- package/dist/email/email-receiver.d.mts.map +1 -0
- package/dist/email/email-receiver.mjs +23 -0
- package/dist/email/email-receiver.mjs.map +1 -0
- package/dist/email/email-sender.d.mts +13 -0
- package/dist/email/email-sender.d.mts.map +1 -0
- package/dist/email/email-sender.mjs +35 -0
- package/dist/email/email-sender.mjs.map +1 -0
- package/dist/email/email-service.respec.d.mts +2 -0
- package/dist/email/email-service.respec.d.mts.map +1 -0
- package/dist/email/email-service.respec.mjs +115 -0
- package/dist/email/email-service.respec.mjs.map +1 -0
- package/dist/email/email-types.d.mts +53 -0
- package/dist/email/email-types.d.mts.map +1 -0
- package/dist/email/email-types.mjs +5 -0
- package/dist/email/email-types.mjs.map +1 -0
- package/dist/email/index.d.mts +11 -0
- package/dist/email/index.d.mts.map +1 -0
- package/dist/email/index.mjs +11 -0
- package/dist/email/index.mjs.map +1 -0
- package/dist/email/mock/email-sender-mock.d.mts +20 -0
- package/dist/email/mock/email-sender-mock.d.mts.map +1 -0
- package/dist/email/mock/email-sender-mock.mjs +47 -0
- package/dist/email/mock/email-sender-mock.mjs.map +1 -0
- package/dist/email/mock/index.d.mts +5 -0
- package/dist/email/mock/index.d.mts.map +1 -0
- package/dist/email/mock/index.mjs +5 -0
- package/dist/email/mock/index.mjs.map +1 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +14 -0
- package/dist/index.mjs.map +1 -0
- package/dist/respec-gib.node.mjs +3 -4
- package/dist/respec-gib.node.mjs.map +1 -1
- package/dist/serve-gib/bootstrap-helper.d.mts +27 -0
- package/dist/serve-gib/bootstrap-helper.d.mts.map +1 -0
- package/dist/serve-gib/bootstrap-helper.mjs +116 -0
- package/dist/serve-gib/bootstrap-helper.mjs.map +1 -0
- package/dist/serve-gib/bootstrap-helper.respec.d.mts +2 -0
- package/dist/serve-gib/bootstrap-helper.respec.d.mts.map +1 -0
- package/dist/serve-gib/bootstrap-helper.respec.mjs +85 -0
- package/dist/serve-gib/bootstrap-helper.respec.mjs.map +1 -0
- package/dist/serve-gib/constants.d.mts +2 -0
- package/dist/serve-gib/constants.d.mts.map +1 -0
- package/dist/serve-gib/constants.mjs +2 -0
- package/dist/serve-gib/constants.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/debug/ws-echo.handler.d.mts +39 -0
- package/dist/serve-gib/handlers/api/debug/ws-echo.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/debug/ws-echo.handler.mjs +99 -0
- package/dist/serve-gib/handlers/api/debug/ws-echo.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/email/email-inbound.handler.d.mts +25 -0
- package/dist/serve-gib/handlers/api/email/email-inbound.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/email/email-inbound.handler.mjs +54 -0
- package/dist/serve-gib/handlers/api/email/email-inbound.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/email/email-outbound.handler.d.mts +24 -0
- package/dist/serve-gib/handlers/api/email/email-outbound.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs +72 -0
- package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/health.handler.d.mts +14 -0
- package/dist/serve-gib/handlers/api/health.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/health.handler.mjs +21 -0
- package/dist/serve-gib/handlers/api/health.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/health.respec.d.mts +2 -0
- package/dist/serve-gib/handlers/api/health.respec.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/health.respec.mjs +47 -0
- package/dist/serve-gib/handlers/api/health.respec.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib-handler-types.d.mts +41 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib-handler-types.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib-handler-types.mjs +34 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib-handler-types.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib.handler.d.mts +26 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib.handler.mjs +179 -0
- package/dist/serve-gib/handlers/api/ibgib/ibgib.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.d.mts +23 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs +52 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.d.mts +44 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs +127 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.d.mts +45 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs +159 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.handler.d.mts +25 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.handler.mjs +183 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts +2 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs +98 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-handler-types.d.mts +22 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-handler-types.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-handler-types.mjs +19 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-handler-types.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.d.mts +19 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs +82 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.d.mts +20 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs +77 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts +2 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs +131 -0
- package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-config.handler.d.mts +23 -0
- package/dist/serve-gib/handlers/api/keystone/sso-config.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs +62 -0
- package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-link.handler.d.mts +23 -0
- package/dist/serve-gib/handlers/api/keystone/sso-link.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs +118 -0
- package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts +24 -0
- package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs +160 -0
- package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/error-handler.d.mts +14 -0
- package/dist/serve-gib/handlers/error-handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/error-handler.mjs +35 -0
- package/dist/serve-gib/handlers/error-handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/handler-base.d.mts +129 -0
- package/dist/serve-gib/handlers/handler-base.d.mts.map +1 -0
- package/dist/serve-gib/handlers/handler-base.mjs +402 -0
- package/dist/serve-gib/handlers/handler-base.mjs.map +1 -0
- package/dist/serve-gib/handlers/static-handler.d.mts +17 -0
- package/dist/serve-gib/handlers/static-handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/static-handler.mjs +85 -0
- package/dist/serve-gib/handlers/static-handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade-handler-base.d.mts +35 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade-handler-base.d.mts.map +1 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade-handler-base.mjs +219 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade-handler-base.mjs.map +1 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade.handler.d.mts +21 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade.handler.d.mts.map +1 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade.handler.mjs +21 -0
- package/dist/serve-gib/handlers/ws/sync-upgrade.handler.mjs.map +1 -0
- package/dist/serve-gib/handlers/ws/ws-helper.d.mts +29 -0
- package/dist/serve-gib/handlers/ws/ws-helper.d.mts.map +1 -0
- package/dist/serve-gib/handlers/ws/ws-helper.mjs +195 -0
- package/dist/serve-gib/handlers/ws/ws-helper.mjs.map +1 -0
- package/dist/serve-gib/handlers/ws/ws-types.d.mts +32 -0
- package/dist/serve-gib/handlers/ws/ws-types.d.mts.map +1 -0
- package/dist/serve-gib/handlers/ws/ws-types.mjs +2 -0
- package/dist/serve-gib/handlers/ws/ws-types.mjs.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-constants.d.mts +4 -0
- package/dist/serve-gib/identity-index/identity-index-constants.d.mts.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-constants.mjs +4 -0
- package/dist/serve-gib/identity-index/identity-index-constants.mjs.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-helpers.d.mts +35 -0
- package/dist/serve-gib/identity-index/identity-index-helpers.d.mts.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-helpers.mjs +69 -0
- package/dist/serve-gib/identity-index/identity-index-helpers.mjs.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-service.d.mts +41 -0
- package/dist/serve-gib/identity-index/identity-index-service.d.mts.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-service.mjs +86 -0
- package/dist/serve-gib/identity-index/identity-index-service.mjs.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts +2 -0
- package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-service.respec.mjs +109 -0
- package/dist/serve-gib/identity-index/identity-index-service.respec.mjs.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-types.d.mts +31 -0
- package/dist/serve-gib/identity-index/identity-index-types.d.mts.map +1 -0
- package/dist/serve-gib/identity-index/identity-index-types.mjs +2 -0
- package/dist/serve-gib/identity-index/identity-index-types.mjs.map +1 -0
- package/dist/serve-gib/index.d.mts +50 -0
- package/dist/serve-gib/index.d.mts.map +1 -0
- package/dist/serve-gib/index.mjs +80 -0
- package/dist/serve-gib/index.mjs.map +1 -0
- package/dist/serve-gib/keystone-schema-helpers.d.mts +50 -0
- package/dist/serve-gib/keystone-schema-helpers.d.mts.map +1 -0
- package/dist/serve-gib/keystone-schema-helpers.mjs +99 -0
- package/dist/serve-gib/keystone-schema-helpers.mjs.map +1 -0
- package/dist/serve-gib/keystone-schema-helpers.respec.d.mts +2 -0
- package/dist/serve-gib/keystone-schema-helpers.respec.d.mts.map +1 -0
- package/dist/serve-gib/keystone-schema-helpers.respec.mjs +68 -0
- package/dist/serve-gib/keystone-schema-helpers.respec.mjs.map +1 -0
- package/dist/serve-gib/metaspace-nodeindexedspace/metaspace-nodeindexedspace.d.mts +38 -0
- package/dist/serve-gib/metaspace-nodeindexedspace/metaspace-nodeindexedspace.d.mts.map +1 -0
- package/dist/serve-gib/metaspace-nodeindexedspace/metaspace-nodeindexedspace.mjs +85 -0
- package/dist/serve-gib/metaspace-nodeindexedspace/metaspace-nodeindexedspace.mjs.map +1 -0
- package/dist/serve-gib/path-constants.d.mts +102 -0
- package/dist/serve-gib/path-constants.d.mts.map +1 -0
- package/dist/serve-gib/path-constants.mjs +107 -0
- package/dist/serve-gib/path-constants.mjs.map +1 -0
- package/dist/serve-gib/path-helper.d.mts +23 -0
- package/dist/serve-gib/path-helper.d.mts.map +1 -0
- package/dist/serve-gib/path-helper.mjs +107 -0
- package/dist/serve-gib/path-helper.mjs.map +1 -0
- package/dist/serve-gib/path-helper.respec.d.mts +2 -0
- package/dist/serve-gib/path-helper.respec.d.mts.map +1 -0
- package/dist/serve-gib/path-helper.respec.mjs +83 -0
- package/dist/serve-gib/path-helper.respec.mjs.map +1 -0
- package/dist/serve-gib/serve-gib-helpers.d.mts +14 -0
- package/dist/serve-gib/serve-gib-helpers.d.mts.map +1 -0
- package/dist/serve-gib/serve-gib-helpers.mjs +30 -0
- package/dist/serve-gib/serve-gib-helpers.mjs.map +1 -0
- package/dist/serve-gib/serve-gib-v1.d.mts +40 -0
- package/dist/serve-gib/serve-gib-v1.d.mts.map +1 -0
- package/dist/serve-gib/serve-gib-v1.mjs +157 -0
- package/dist/serve-gib/serve-gib-v1.mjs.map +1 -0
- package/dist/serve-gib/serve-gib.respec.d.mts +2 -0
- package/dist/serve-gib/serve-gib.respec.d.mts.map +1 -0
- package/dist/serve-gib/serve-gib.respec.mjs +85 -0
- package/dist/serve-gib/serve-gib.respec.mjs.map +1 -0
- package/dist/serve-gib/server-constants.d.mts +3 -0
- package/dist/serve-gib/server-constants.d.mts.map +1 -0
- package/dist/serve-gib/server-constants.mjs +3 -0
- package/dist/serve-gib/server-constants.mjs.map +1 -0
- package/dist/serve-gib/types.d.mts +94 -0
- package/dist/serve-gib/types.d.mts.map +1 -0
- package/dist/serve-gib/types.mjs +7 -0
- package/dist/serve-gib/types.mjs.map +1 -0
- package/dist/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace-helper.mjs.map +1 -1
- package/dist/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace.d.mts +5 -0
- package/dist/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace.d.mts.map +1 -1
- package/dist/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace.mjs +37 -0
- package/dist/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-filesystem-space-helper.mjs +1 -1
- package/dist/witness/space/node-filesystem-space/node-filesystem-space-helper.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-filesystem-space-types.d.mts.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-filesystem-space-v1.d.mts.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-filesystem-space-v1.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/node-indexed-filesystem-space-v1.d.mts.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs.map +1 -1
- package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs.map +1 -1
- package/package.json +2 -2
- package/src/email/aws/aws-sigv4-helper.mts +139 -0
- package/src/email/aws/email-receiver-aws.mts +107 -0
- package/src/email/aws/email-sender-aws.mts +130 -0
- package/src/email/aws/index.mts +7 -0
- package/src/email/email-constants.mts +18 -0
- package/src/email/email-helpers.mts +160 -0
- package/src/email/email-receiver.mts +29 -0
- package/src/email/email-sender.mts +44 -0
- package/src/email/email-service.respec.mts +134 -0
- package/src/email/email-types.mts +59 -0
- package/src/email/index.mts +11 -0
- package/src/email/mock/email-sender-mock.mts +54 -0
- package/src/email/mock/index.mts +5 -0
- package/src/index.mts +41 -0
- package/src/respec-gib.node.mts +4 -5
- package/src/serve-gib/CHANGELOG.md +34 -0
- package/src/serve-gib/README.md +77 -0
- package/src/serve-gib/SERVE_REFACTOR_IMPLEMENTATION.md +112 -0
- package/src/serve-gib/bootstrap-helper.mts +141 -0
- package/src/serve-gib/bootstrap-helper.respec.mts +100 -0
- package/src/serve-gib/constants.mts +1 -0
- package/src/serve-gib/handlers/api/debug/ws-echo.handler.mts +105 -0
- package/src/serve-gib/handlers/api/email/email-inbound.handler.mts +67 -0
- package/src/serve-gib/handlers/api/email/email-outbound.handler.mts +82 -0
- package/src/serve-gib/handlers/api/health.handler.mts +23 -0
- package/src/serve-gib/handlers/api/health.respec.mts +51 -0
- package/src/serve-gib/handlers/api/ibgib/ibgib-handler-types.mts +49 -0
- package/src/serve-gib/handlers/api/ibgib/ibgib.handler.mts +176 -0
- package/src/serve-gib/handlers/api/keystone/keystone-check-email.handler.mts +60 -0
- package/src/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +150 -0
- package/src/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +171 -0
- package/src/serve-gib/handlers/api/keystone/keystone-get.handler.mts +198 -0
- package/src/serve-gib/handlers/api/keystone/keystone-get.respec.mts +106 -0
- package/src/serve-gib/handlers/api/keystone/keystone-handler-types.mts +29 -0
- package/src/serve-gib/handlers/api/keystone/keystone-lookup.handler.mts +95 -0
- package/src/serve-gib/handlers/api/keystone/keystone-post.handler.mts +75 -0
- package/src/serve-gib/handlers/api/keystone/keystone-post.respec.mts +130 -0
- package/src/serve-gib/handlers/api/keystone/sso-config.handler.mts +66 -0
- package/src/serve-gib/handlers/api/keystone/sso-link.handler.mts +140 -0
- package/src/serve-gib/handlers/api/keystone/sso-login.handler.mts +190 -0
- package/src/serve-gib/handlers/error-handler.mts +36 -0
- package/src/serve-gib/handlers/handler-base.mts +383 -0
- package/src/serve-gib/handlers/static-handler.mts +83 -0
- package/src/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +233 -0
- package/src/serve-gib/handlers/ws/sync-upgrade.handler.mts +24 -0
- package/src/serve-gib/handlers/ws/ws-helper.mts +216 -0
- package/src/serve-gib/handlers/ws/ws-types.mts +53 -0
- package/src/serve-gib/identity-index/identity-index-constants.mts +3 -0
- package/src/serve-gib/identity-index/identity-index-helpers.mts +85 -0
- package/src/serve-gib/identity-index/identity-index-service.mts +109 -0
- package/src/serve-gib/identity-index/identity-index-service.respec.mts +135 -0
- package/src/serve-gib/identity-index/identity-index-types.mts +33 -0
- package/src/serve-gib/index.mts +92 -0
- package/src/serve-gib/keystone-schema-helpers.mts +120 -0
- package/src/serve-gib/keystone-schema-helpers.respec.mts +85 -0
- package/src/serve-gib/metaspace-nodeindexedspace/metaspace-nodeindexedspace.mts +109 -0
- package/src/serve-gib/path-constants.mts +119 -0
- package/src/serve-gib/path-helper.mts +101 -0
- package/src/serve-gib/path-helper.respec.mts +99 -0
- package/src/serve-gib/serve-gib-helpers.mts +32 -0
- package/src/serve-gib/serve-gib-v1.mts +172 -0
- package/src/serve-gib/serve-gib.respec.mts +90 -0
- package/src/serve-gib/server-constants.mts +2 -0
- package/src/serve-gib/types.mts +102 -0
- package/src/witness/space/metaspace/metaspace-nodespace/metaspace-nodespace.mts +41 -0
- package/src/witness/space/node-filesystem-space/node-filesystem-space-helper.mts +1 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Signature Version 4 helper using native Node.js crypto module.
|
|
3
|
+
* Zero-dependency implementation for signing REST requests to AWS services (e.g., SES).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
|
|
8
|
+
export interface AwsSigV4Options {
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
region: string;
|
|
12
|
+
service: string;
|
|
13
|
+
accessKeyId: string;
|
|
14
|
+
secretAccessKey: string;
|
|
15
|
+
body?: string | Buffer;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
timestamp?: Date;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AwsSigV4Result {
|
|
21
|
+
headers: Record<string, string>;
|
|
22
|
+
amzDate: string;
|
|
23
|
+
signature: string;
|
|
24
|
+
authorization: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sha256Hex(data: string | Buffer): string {
|
|
28
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hmac(key: Buffer | string, data: string): Buffer {
|
|
32
|
+
return crypto.createHmac('sha256', key).update(data, 'utf8').digest();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function encodeRfc3986(str: string): string {
|
|
36
|
+
return encodeURIComponent(str).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Computes AWS SigV4 signed headers for an HTTP request.
|
|
41
|
+
*/
|
|
42
|
+
export function createAwsSigV4Headers(options: AwsSigV4Options): AwsSigV4Result {
|
|
43
|
+
const {
|
|
44
|
+
method,
|
|
45
|
+
url: urlStr,
|
|
46
|
+
region,
|
|
47
|
+
service,
|
|
48
|
+
accessKeyId,
|
|
49
|
+
secretAccessKey,
|
|
50
|
+
body = '',
|
|
51
|
+
headers: extraHeaders = {},
|
|
52
|
+
timestamp = new Date(),
|
|
53
|
+
} = options;
|
|
54
|
+
|
|
55
|
+
const parsedUrl = new URL(urlStr);
|
|
56
|
+
const amzDate = timestamp.toISOString().replace(/[:-]/g, '').replace(/\.\d{3}/, '');
|
|
57
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
58
|
+
|
|
59
|
+
const headersToSign: Record<string, string> = {
|
|
60
|
+
host: parsedUrl.host,
|
|
61
|
+
'x-amz-date': amzDate,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
for (const [k, v] of Object.entries(extraHeaders)) {
|
|
65
|
+
headersToSign[k.toLowerCase()] = v.trim().replace(/\s+/g, ' ');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const sortedHeaderKeys = Object.keys(headersToSign).sort();
|
|
69
|
+
const canonicalHeaders = sortedHeaderKeys.map(k => `${k}:${headersToSign[k]}\n`).join('');
|
|
70
|
+
const signedHeaders = sortedHeaderKeys.join(';');
|
|
71
|
+
|
|
72
|
+
// Canonical URI
|
|
73
|
+
let canonicalUri = parsedUrl.pathname;
|
|
74
|
+
if (!canonicalUri) {
|
|
75
|
+
canonicalUri = '/';
|
|
76
|
+
} else {
|
|
77
|
+
canonicalUri = canonicalUri
|
|
78
|
+
.split('/')
|
|
79
|
+
.map(segment => encodeRfc3986(segment))
|
|
80
|
+
.join('/');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Canonical Query String
|
|
84
|
+
const queryParams: [string, string][] = [];
|
|
85
|
+
parsedUrl.searchParams.forEach((val, key) => {
|
|
86
|
+
queryParams.push([encodeRfc3986(key), encodeRfc3986(val)]);
|
|
87
|
+
});
|
|
88
|
+
queryParams.sort(([k1, v1], [k2, v2]) => {
|
|
89
|
+
if (k1 === k2) { return v1.localeCompare(v2); }
|
|
90
|
+
return k1.localeCompare(k2);
|
|
91
|
+
});
|
|
92
|
+
const canonicalQueryString = queryParams.map(([k, v]) => `${k}=${v}`).join('&');
|
|
93
|
+
|
|
94
|
+
const payloadHash = sha256Hex(body);
|
|
95
|
+
|
|
96
|
+
const canonicalRequest = [
|
|
97
|
+
method.toUpperCase(),
|
|
98
|
+
canonicalUri,
|
|
99
|
+
canonicalQueryString,
|
|
100
|
+
canonicalHeaders,
|
|
101
|
+
signedHeaders,
|
|
102
|
+
payloadHash,
|
|
103
|
+
].join('\n');
|
|
104
|
+
|
|
105
|
+
const hashedCanonicalRequest = sha256Hex(canonicalRequest);
|
|
106
|
+
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
|
|
107
|
+
|
|
108
|
+
const stringToSign = [
|
|
109
|
+
'AWS4-HMAC-SHA256',
|
|
110
|
+
amzDate,
|
|
111
|
+
credentialScope,
|
|
112
|
+
hashedCanonicalRequest,
|
|
113
|
+
].join('\n');
|
|
114
|
+
|
|
115
|
+
const kDate = hmac(`AWS4${secretAccessKey}`, dateStamp);
|
|
116
|
+
const kRegion = hmac(kDate, region);
|
|
117
|
+
const kService = hmac(kRegion, service);
|
|
118
|
+
const kSigning = hmac(kService, 'aws4_request');
|
|
119
|
+
|
|
120
|
+
const signature = crypto.createHmac('sha256', kSigning).update(stringToSign, 'utf8').digest('hex');
|
|
121
|
+
const authorization = `AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
122
|
+
|
|
123
|
+
const resultHeaders: Record<string, string> = {
|
|
124
|
+
Host: parsedUrl.host,
|
|
125
|
+
'X-Amz-Date': amzDate,
|
|
126
|
+
Authorization: authorization,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
for (const [k, v] of Object.entries(extraHeaders)) {
|
|
130
|
+
resultHeaders[k] = v;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
headers: resultHeaders,
|
|
135
|
+
amzDate,
|
|
136
|
+
signature,
|
|
137
|
+
authorization,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SES concrete driver implementation of EmailReceiverBase.
|
|
3
|
+
* Parses SES notification webhooks (SNS / S3) and evaluates spam/SPF/DKIM/virus verdicts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseEmailAddress, sanitizeHeaderValue } from '../email-helpers.mjs';
|
|
7
|
+
import { EmailReceiverBase } from '../email-receiver.mjs';
|
|
8
|
+
import { EmailSpamVerdicts, InboundEmail } from '../email-types.mjs';
|
|
9
|
+
|
|
10
|
+
export class EmailReceiverAws extends EmailReceiverBase {
|
|
11
|
+
/**
|
|
12
|
+
* Parses an incoming AWS SES webhook or SNS notification payload into a normalized InboundEmail object.
|
|
13
|
+
*/
|
|
14
|
+
async parseInboundPayload(payload: any): Promise<InboundEmail> {
|
|
15
|
+
let rawData = payload;
|
|
16
|
+
|
|
17
|
+
if (typeof rawData === 'string') {
|
|
18
|
+
try {
|
|
19
|
+
rawData = JSON.parse(rawData);
|
|
20
|
+
} catch {
|
|
21
|
+
throw new Error('Invalid JSON string in AWS inbound payload. (E: 6f7890123456789abcdef6a7b8c9d0e1)');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Validate SNS SigningCertURL domain if present (prevents SSRF / unauthenticated cert host spoofing)
|
|
26
|
+
if (rawData && typeof rawData === 'object' && rawData.SigningCertURL) {
|
|
27
|
+
try {
|
|
28
|
+
const certUrl = new URL(rawData.SigningCertURL);
|
|
29
|
+
if (certUrl.protocol !== 'https:' || !certUrl.hostname.endsWith('.amazonaws.com')) {
|
|
30
|
+
throw new Error(`Invalid SNS SigningCertURL host: ${rawData.SigningCertURL} (E: a9b8c7d6e5f432109876543210fedcba)`);
|
|
31
|
+
}
|
|
32
|
+
} catch (err: any) {
|
|
33
|
+
if (err?.message?.includes('(E: ')) { throw err; }
|
|
34
|
+
throw new Error(`Invalid SNS SigningCertURL: ${err?.message || String(err)} (E: b8c7d6e5f432109876543210fedcba9)`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If payload is wrapped inside an SNS message: { Type: 'Notification', Message: '{...}' }
|
|
39
|
+
if (rawData && typeof rawData === 'object' && rawData.Message && typeof rawData.Message === 'string') {
|
|
40
|
+
try {
|
|
41
|
+
rawData = JSON.parse(rawData.Message);
|
|
42
|
+
} catch {
|
|
43
|
+
// If Message is not JSON, retain outer object
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!rawData || typeof rawData !== 'object') {
|
|
48
|
+
throw new Error('Invalid payload structure for AWS SES email parsing. (E: 7890123456789abcdef7a8b9c0d1e2f3)');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const mail = rawData.mail || {};
|
|
52
|
+
const receipt = rawData.receipt || {};
|
|
53
|
+
|
|
54
|
+
const messageId = mail.messageId || rawData.messageId || `aws-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
55
|
+
const receivedAt = mail.timestamp || receipt.timestamp || new Date().toISOString();
|
|
56
|
+
|
|
57
|
+
// Extract From
|
|
58
|
+
let fromRaw = mail.source || mail.commonHeaders?.from?.[0] || rawData.from || 'unknown@unknown.com';
|
|
59
|
+
if (Array.isArray(fromRaw)) { fromRaw = fromRaw[0]; }
|
|
60
|
+
const from = parseEmailAddress(fromRaw);
|
|
61
|
+
|
|
62
|
+
// Extract To
|
|
63
|
+
const toRawList = mail.destination || mail.commonHeaders?.to || rawData.to || [];
|
|
64
|
+
const toList: string[] = Array.isArray(toRawList) ? toRawList : [toRawList];
|
|
65
|
+
const to = toList.map(t => parseEmailAddress(t));
|
|
66
|
+
|
|
67
|
+
// Extract Cc
|
|
68
|
+
const ccRawList = mail.commonHeaders?.cc || rawData.cc;
|
|
69
|
+
const cc = ccRawList ? (Array.isArray(ccRawList) ? ccRawList : [ccRawList]).map(c => parseEmailAddress(c)) : undefined;
|
|
70
|
+
|
|
71
|
+
// Subject (sanitized against header injection)
|
|
72
|
+
const subjectRaw = mail.commonHeaders?.subject || rawData.subject || '(No Subject)';
|
|
73
|
+
const subject = sanitizeHeaderValue(subjectRaw);
|
|
74
|
+
|
|
75
|
+
// Extract Verdicts
|
|
76
|
+
const spamVerdicts: EmailSpamVerdicts = {
|
|
77
|
+
spamVerdict: receipt.spamVerdict?.status || rawData.spamVerdict || 'PASS',
|
|
78
|
+
virusVerdict: receipt.virusVerdict?.status || rawData.virusVerdict || 'PASS',
|
|
79
|
+
spfVerdict: receipt.spfVerdict?.status || rawData.spfVerdict || 'PASS',
|
|
80
|
+
dkimVerdict: receipt.dkimVerdict?.status || rawData.dkimVerdict || 'PASS',
|
|
81
|
+
dmarcVerdict: receipt.dmarcVerdict?.status || rawData.dmarcVerdict || 'PASS',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const folder = this.evaluateSpamVerdicts(spamVerdicts);
|
|
85
|
+
|
|
86
|
+
const textBody = rawData.textBody || rawData.content || undefined;
|
|
87
|
+
const htmlBody = rawData.htmlBody || undefined;
|
|
88
|
+
const rawMime = rawData.rawMime || (typeof rawData.content === 'string' ? rawData.content : undefined);
|
|
89
|
+
|
|
90
|
+
const inboundEmail: InboundEmail = {
|
|
91
|
+
messageId,
|
|
92
|
+
from,
|
|
93
|
+
to,
|
|
94
|
+
cc,
|
|
95
|
+
subject,
|
|
96
|
+
textBody,
|
|
97
|
+
htmlBody,
|
|
98
|
+
rawMime,
|
|
99
|
+
receivedAt,
|
|
100
|
+
spamVerdicts,
|
|
101
|
+
folder,
|
|
102
|
+
headers: mail.commonHeaders || rawData.headers,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return inboundEmail;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SES concrete driver implementation of EmailSenderBase.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_AWS_REGION, DEFAULT_AWS_SERVICE, DEFAULT_AWS_SES_ENDPOINT } from '../email-constants.mjs';
|
|
6
|
+
import { formatEmailAddress } from '../email-helpers.mjs';
|
|
7
|
+
import { EmailSenderBase } from '../email-sender.mjs';
|
|
8
|
+
import { EmailSendResult, OutboundEmail } from '../email-types.mjs';
|
|
9
|
+
import { createAwsSigV4Headers } from './aws-sigv4-helper.mjs';
|
|
10
|
+
|
|
11
|
+
export interface EmailSenderAwsOptions {
|
|
12
|
+
accessKeyId: string;
|
|
13
|
+
secretAccessKey: string;
|
|
14
|
+
region?: string;
|
|
15
|
+
endpoint?: string;
|
|
16
|
+
/** Custom fetch implementation (optional, defaults to globalThis.fetch) */
|
|
17
|
+
fetchFn?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class EmailSenderAws extends EmailSenderBase {
|
|
21
|
+
private accessKeyId: string;
|
|
22
|
+
private secretAccessKey: string;
|
|
23
|
+
private region: string;
|
|
24
|
+
private endpoint: string;
|
|
25
|
+
private fetchFn: typeof fetch;
|
|
26
|
+
|
|
27
|
+
constructor(options: EmailSenderAwsOptions) {
|
|
28
|
+
super();
|
|
29
|
+
if (!options.accessKeyId || !options.secretAccessKey) {
|
|
30
|
+
throw new Error('EmailSenderAws requires accessKeyId and secretAccessKey. (E: 5e6f7890123456789abcdef5a6b7c8d9)');
|
|
31
|
+
}
|
|
32
|
+
this.accessKeyId = options.accessKeyId;
|
|
33
|
+
this.secretAccessKey = options.secretAccessKey;
|
|
34
|
+
this.region = options.region || DEFAULT_AWS_REGION;
|
|
35
|
+
this.endpoint = options.endpoint || `https://email.${this.region}.amazonaws.com`;
|
|
36
|
+
this.fetchFn = options.fetchFn || globalThis.fetch;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async sendEmail(email: OutboundEmail): Promise<EmailSendResult> {
|
|
40
|
+
try {
|
|
41
|
+
this.validateOutboundEmail(email);
|
|
42
|
+
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
params.append('Action', 'SendEmail');
|
|
45
|
+
params.append('Version', '2010-12-01');
|
|
46
|
+
params.append('Source', formatEmailAddress(email.from));
|
|
47
|
+
|
|
48
|
+
email.to.forEach((addr, idx) => {
|
|
49
|
+
params.append(`Destination.ToAddresses.member.${idx + 1}`, formatEmailAddress(addr));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (email.cc && email.cc.length > 0) {
|
|
53
|
+
email.cc.forEach((addr, idx) => {
|
|
54
|
+
params.append(`Destination.CcAddresses.member.${idx + 1}`, formatEmailAddress(addr));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (email.bcc && email.bcc.length > 0) {
|
|
59
|
+
email.bcc.forEach((addr, idx) => {
|
|
60
|
+
params.append(`Destination.BccAddresses.member.${idx + 1}`, formatEmailAddress(addr));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (email.replyTo) {
|
|
65
|
+
params.append('ReplyToAddresses.member.1', formatEmailAddress(email.replyTo));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
params.append('Message.Subject.Data', email.subject);
|
|
69
|
+
params.append('Message.Subject.Charset', 'UTF-8');
|
|
70
|
+
|
|
71
|
+
if (email.textBody) {
|
|
72
|
+
params.append('Message.Body.Text.Data', email.textBody);
|
|
73
|
+
params.append('Message.Body.Text.Charset', 'UTF-8');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (email.htmlBody) {
|
|
77
|
+
params.append('Message.Body.Html.Data', email.htmlBody);
|
|
78
|
+
params.append('Message.Body.Html.Charset', 'UTF-8');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const bodyString = params.toString();
|
|
82
|
+
const headers = {
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const sigV4Result = createAwsSigV4Headers({
|
|
87
|
+
method: 'POST',
|
|
88
|
+
url: this.endpoint,
|
|
89
|
+
region: this.region,
|
|
90
|
+
service: DEFAULT_AWS_SERVICE,
|
|
91
|
+
accessKeyId: this.accessKeyId,
|
|
92
|
+
secretAccessKey: this.secretAccessKey,
|
|
93
|
+
body: bodyString,
|
|
94
|
+
headers,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const response = await this.fetchFn(this.endpoint, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: sigV4Result.headers,
|
|
100
|
+
body: bodyString,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const responseText = await response.text();
|
|
104
|
+
|
|
105
|
+
if (response.ok) {
|
|
106
|
+
// Parse MessageId from XML response: <MessageId>010001...</MessageId>
|
|
107
|
+
const messageIdMatch = responseText.match(/<MessageId>(.*?)<\/MessageId>/);
|
|
108
|
+
const messageId = messageIdMatch ? messageIdMatch[1] : undefined;
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
messageId,
|
|
112
|
+
statusCode: response.status,
|
|
113
|
+
};
|
|
114
|
+
} else {
|
|
115
|
+
const errorMatch = responseText.match(/<Message>(.*?)<\/Message>/);
|
|
116
|
+
const errorMsg = errorMatch ? errorMatch[1] : responseText;
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
error: `AWS SES Error (${response.status}): ${errorMsg}`,
|
|
120
|
+
statusCode: response.status,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
} catch (error: any) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: error?.message || String(error),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for email infrastructure in node-gib.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_EMAIL_CHARSET = 'UTF-8';
|
|
6
|
+
export const EMAIL_MODE_AWS = 'aws';
|
|
7
|
+
export const EMAIL_MODE_MOCK = 'mock';
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_AWS_REGION = 'us-east-1';
|
|
10
|
+
export const DEFAULT_AWS_SES_ENDPOINT = 'https://email.us-east-1.amazonaws.com';
|
|
11
|
+
export const DEFAULT_AWS_SERVICE = 'ses';
|
|
12
|
+
|
|
13
|
+
export const VERDICT_PASS = 'PASS';
|
|
14
|
+
export const VERDICT_FAIL = 'FAIL';
|
|
15
|
+
export const VERDICT_GRAY = 'GRAY';
|
|
16
|
+
|
|
17
|
+
export const FOLDER_INBOX = 'inbox';
|
|
18
|
+
export const FOLDER_JUNK = 'junk';
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for email parsing, validation, and MIME string formatting.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_EMAIL_CHARSET } from './email-constants.mjs';
|
|
6
|
+
import { EmailAddress, OutboundEmail } from './email-types.mjs';
|
|
7
|
+
|
|
8
|
+
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validates whether an email string matches standard email formatting.
|
|
12
|
+
*/
|
|
13
|
+
export function validateEmail(email: string): boolean {
|
|
14
|
+
if (!email || typeof email !== 'string') { return false; }
|
|
15
|
+
return EMAIL_REGEX.test(email.trim());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Alias for validateEmail for email address format checking. */
|
|
19
|
+
export function validateEmailAddress(email: string): boolean {
|
|
20
|
+
return validateEmail(email);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sanitizes header values by stripping carriage return (\r) and newline (\n) characters
|
|
26
|
+
* to prevent MIME header injection.
|
|
27
|
+
*/
|
|
28
|
+
export function sanitizeHeaderValue(val: string): string {
|
|
29
|
+
if (!val || typeof val !== 'string') { return ''; }
|
|
30
|
+
return val.replace(/[\r\n]/g, '').trim();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parses a string or EmailAddress object into a normalized EmailAddress object.
|
|
35
|
+
* Examples:
|
|
36
|
+
* - "Alice <alice@example.com>" -> { name: "Alice", address: "alice@example.com" }
|
|
37
|
+
* - "alice@example.com" -> { address: "alice@example.com" }
|
|
38
|
+
*/
|
|
39
|
+
export function parseEmailAddress(addr: EmailAddress | string): EmailAddress {
|
|
40
|
+
if (typeof addr === 'object' && addr !== null) {
|
|
41
|
+
return {
|
|
42
|
+
address: sanitizeHeaderValue(addr.address).toLowerCase(),
|
|
43
|
+
name: addr.name ? sanitizeHeaderValue(addr.name) : undefined,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const str = sanitizeHeaderValue(addr || '');
|
|
48
|
+
const match = str.match(/^(?:"?([^"]*)"?\s+)?<([^>]+)>$/);
|
|
49
|
+
if (match) {
|
|
50
|
+
const name = match[1]?.trim();
|
|
51
|
+
const address = match[2]?.trim().toLowerCase();
|
|
52
|
+
return {
|
|
53
|
+
address,
|
|
54
|
+
name: name || undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { address: str.toLowerCase() };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Formats an EmailAddress or string into RFC 2822 format (e.g. `"Alice" <alice@example.com>`).
|
|
63
|
+
*/
|
|
64
|
+
export function formatEmailAddress(addr: EmailAddress | string): string {
|
|
65
|
+
const parsed = parseEmailAddress(addr);
|
|
66
|
+
if (parsed.name) {
|
|
67
|
+
return `"${parsed.name}" <${parsed.address}>`;
|
|
68
|
+
}
|
|
69
|
+
return parsed.address;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Parses raw MIME headers string or header record into a key-value record with normalized header names.
|
|
74
|
+
*/
|
|
75
|
+
export function parseRawMimeHeaders(raw: string | Record<string, string>): Record<string, string> {
|
|
76
|
+
if (typeof raw === 'object' && raw !== null) {
|
|
77
|
+
return { ...raw };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const headers: Record<string, string> = {};
|
|
81
|
+
if (!raw || typeof raw !== 'string') { return headers; }
|
|
82
|
+
|
|
83
|
+
// Header block ends at double newline
|
|
84
|
+
const headerBlock = raw.split(/\r?\n\r?\n/)[0] || raw;
|
|
85
|
+
// Unfold multi-line headers
|
|
86
|
+
const unfolded = headerBlock.replace(/\r?\n[ \t]+/g, ' ');
|
|
87
|
+
const lines = unfolded.split(/\r?\n/);
|
|
88
|
+
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
const colonIdx = line.indexOf(':');
|
|
91
|
+
if (colonIdx > 0) {
|
|
92
|
+
const key = line.slice(0, colonIdx).trim();
|
|
93
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
94
|
+
headers[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return headers;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Formats an OutboundEmail object into a raw MIME string (RFC 2822).
|
|
103
|
+
*/
|
|
104
|
+
export function formatRawMimeString(email: OutboundEmail, charset: string = DEFAULT_EMAIL_CHARSET): string {
|
|
105
|
+
const fromStr = formatEmailAddress(email.from);
|
|
106
|
+
const toStrs = email.to.map(t => formatEmailAddress(t)).join(', ');
|
|
107
|
+
|
|
108
|
+
const lines: string[] = [
|
|
109
|
+
`From: ${fromStr}`,
|
|
110
|
+
`To: ${toStrs}`,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
if (email.cc && email.cc.length > 0) {
|
|
114
|
+
lines.push(`Cc: ${email.cc.map(c => formatEmailAddress(c)).join(', ')}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (email.replyTo) {
|
|
118
|
+
lines.push(`Reply-To: ${formatEmailAddress(email.replyTo)}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
lines.push(`Subject: ${sanitizeHeaderValue(email.subject)}`);
|
|
122
|
+
|
|
123
|
+
if (email.headers) {
|
|
124
|
+
for (const [key, val] of Object.entries(email.headers)) {
|
|
125
|
+
lines.push(`${sanitizeHeaderValue(key)}: ${sanitizeHeaderValue(val)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const hasText = Boolean(email.textBody);
|
|
130
|
+
const hasHtml = Boolean(email.htmlBody);
|
|
131
|
+
|
|
132
|
+
if (hasText && hasHtml) {
|
|
133
|
+
const boundary = `----=_Part_${Date.now()}_${Math.random().toString(36).substring(2)}`;
|
|
134
|
+
lines.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push(`--${boundary}`);
|
|
137
|
+
lines.push(`Content-Type: text/plain; charset=${charset}`);
|
|
138
|
+
lines.push('Content-Transfer-Encoding: 7bit');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push(email.textBody || '');
|
|
141
|
+
lines.push(`--${boundary}`);
|
|
142
|
+
lines.push(`Content-Type: text/html; charset=${charset}`);
|
|
143
|
+
lines.push('Content-Transfer-Encoding: 7bit');
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push(email.htmlBody || '');
|
|
146
|
+
lines.push(`--${boundary}--`);
|
|
147
|
+
} else if (hasHtml) {
|
|
148
|
+
lines.push(`Content-Type: text/html; charset=${charset}`);
|
|
149
|
+
lines.push('Content-Transfer-Encoding: 7bit');
|
|
150
|
+
lines.push('');
|
|
151
|
+
lines.push(email.htmlBody || '');
|
|
152
|
+
} else {
|
|
153
|
+
lines.push(`Content-Type: text/plain; charset=${charset}`);
|
|
154
|
+
lines.push('Content-Transfer-Encoding: 7bit');
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push(email.textBody || '');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return lines.join('\r\n');
|
|
160
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for EmailReceiver drivers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { FOLDER_INBOX, FOLDER_JUNK, VERDICT_FAIL } from './email-constants.mjs';
|
|
6
|
+
import { EmailReceiver, EmailSpamVerdicts, InboundEmail } from './email-types.mjs';
|
|
7
|
+
|
|
8
|
+
export abstract class EmailReceiverBase implements EmailReceiver {
|
|
9
|
+
abstract parseInboundPayload(payload: any): Promise<InboundEmail>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates spam/SPF/DKIM/virus verdicts to determine folder classification ("inbox" vs "junk").
|
|
13
|
+
*/
|
|
14
|
+
evaluateSpamVerdicts(verdicts: EmailSpamVerdicts): 'inbox' | 'junk' {
|
|
15
|
+
if (!verdicts) { return FOLDER_INBOX as 'inbox'; }
|
|
16
|
+
|
|
17
|
+
const { spamVerdict, virusVerdict, spfVerdict, dkimVerdict } = verdicts;
|
|
18
|
+
if (
|
|
19
|
+
spamVerdict === VERDICT_FAIL ||
|
|
20
|
+
virusVerdict === VERDICT_FAIL ||
|
|
21
|
+
spfVerdict === VERDICT_FAIL ||
|
|
22
|
+
dkimVerdict === VERDICT_FAIL
|
|
23
|
+
) {
|
|
24
|
+
return FOLDER_JUNK as 'junk';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return FOLDER_INBOX as 'inbox';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for EmailSender drivers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { parseEmailAddress, validateEmail } from './email-helpers.mjs';
|
|
6
|
+
import { EmailSender, EmailSendResult, OutboundEmail } from './email-types.mjs';
|
|
7
|
+
|
|
8
|
+
export abstract class EmailSenderBase implements EmailSender {
|
|
9
|
+
abstract sendEmail(email: OutboundEmail): Promise<EmailSendResult>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validates required fields on an outbound email object.
|
|
13
|
+
* Throws an error if required fields are missing or invalid.
|
|
14
|
+
*/
|
|
15
|
+
protected validateOutboundEmail(email: OutboundEmail): void {
|
|
16
|
+
if (!email) {
|
|
17
|
+
throw new Error('OutboundEmail is required. (E: 8f4e2a1b9c3d4e5f6a7b8c9d0e1f2a3b)');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const fromParsed = parseEmailAddress(email.from);
|
|
21
|
+
if (!fromParsed.address || !validateEmail(fromParsed.address)) {
|
|
22
|
+
throw new Error(`Invalid "from" email address: ${fromParsed.address} (E: a1b2c3d4e5f67890123456789abcdef0)`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!email.to || !Array.isArray(email.to) || email.to.length === 0) {
|
|
26
|
+
throw new Error('Outbound email must contain at least one recipient in "to". (E: 1a2b3c4d5e6f7890123456789abcdef1)');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const recipient of email.to) {
|
|
30
|
+
const parsed = parseEmailAddress(recipient);
|
|
31
|
+
if (!parsed.address || !validateEmail(parsed.address)) {
|
|
32
|
+
throw new Error(`Invalid "to" email address: ${parsed.address} (E: 2b3c4d5e6f7890123456789abcdef2)`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!email.subject || typeof email.subject !== 'string') {
|
|
37
|
+
throw new Error('Outbound email must contain a "subject" string. (E: 3c4d5e6f7890123456789abcdef3)');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!email.textBody && !email.htmlBody) {
|
|
41
|
+
throw new Error('Outbound email must contain either "textBody" or "htmlBody". (E: 4d5e6f7890123456789abcdef4)');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|