Initial
commit
0066e66a47
|
@ -0,0 +1,409 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 56;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
2AD701EB2B9EAA970058791B /* commentsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD701EA2B9EAA970058791B /* commentsApp.swift */; };
|
||||||
|
2AD701ED2B9EAA970058791B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD701EC2B9EAA970058791B /* ContentView.swift */; };
|
||||||
|
2AD701EF2B9EAA990058791B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD701EE2B9EAA990058791B /* Assets.xcassets */; };
|
||||||
|
2AD701F22B9EAA990058791B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD701F12B9EAA990058791B /* Preview Assets.xcassets */; };
|
||||||
|
2AD701FA2B9EAB540058791B /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 2AD701F92B9EAB540058791B /* Alamofire */; };
|
||||||
|
2AD701FC2B9EAB540058791B /* AlamofireDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2AD701FB2B9EAB540058791B /* AlamofireDynamic */; };
|
||||||
|
2AD701FE2B9EAFF00058791B /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD701FD2B9EAFF00058791B /* Api.swift */; };
|
||||||
|
2AF729782B9F060200B4C04E /* DynamicColor in Frameworks */ = {isa = PBXBuildFile; productRef = 2AF729772B9F060200B4C04E /* DynamicColor */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
2AD701E72B9EAA970058791B /* comments.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = comments.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
2AD701EA2B9EAA970058791B /* commentsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = commentsApp.swift; sourceTree = "<group>"; };
|
||||||
|
2AD701EC2B9EAA970058791B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
|
2AD701EE2B9EAA990058791B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
2AD701F12B9EAA990058791B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
|
2AD701FD2B9EAFF00058791B /* Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = "<group>"; };
|
||||||
|
2AF729752B9EDA0B00B4C04E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
2AD701E42B9EAA970058791B /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
2AD701FA2B9EAB540058791B /* Alamofire in Frameworks */,
|
||||||
|
2AF729782B9F060200B4C04E /* DynamicColor in Frameworks */,
|
||||||
|
2AD701FC2B9EAB540058791B /* AlamofireDynamic in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
2AD701DE2B9EAA970058791B = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2AD701E92B9EAA970058791B /* comments */,
|
||||||
|
2AD701E82B9EAA970058791B /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2AD701E82B9EAA970058791B /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2AD701E72B9EAA970058791B /* comments.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2AD701E92B9EAA970058791B /* comments */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2AF729752B9EDA0B00B4C04E /* Info.plist */,
|
||||||
|
2AD701EA2B9EAA970058791B /* commentsApp.swift */,
|
||||||
|
2AD701EC2B9EAA970058791B /* ContentView.swift */,
|
||||||
|
2AD701EE2B9EAA990058791B /* Assets.xcassets */,
|
||||||
|
2AD701F02B9EAA990058791B /* Preview Content */,
|
||||||
|
2AD701FD2B9EAFF00058791B /* Api.swift */,
|
||||||
|
);
|
||||||
|
path = comments;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
2AD701F02B9EAA990058791B /* Preview Content */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2AD701F12B9EAA990058791B /* Preview Assets.xcassets */,
|
||||||
|
);
|
||||||
|
path = "Preview Content";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
2AD701E62B9EAA970058791B /* comments */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 2AD701F52B9EAA990058791B /* Build configuration list for PBXNativeTarget "comments" */;
|
||||||
|
buildPhases = (
|
||||||
|
2AD701E32B9EAA970058791B /* Sources */,
|
||||||
|
2AD701E42B9EAA970058791B /* Frameworks */,
|
||||||
|
2AD701E52B9EAA970058791B /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = comments;
|
||||||
|
packageProductDependencies = (
|
||||||
|
2AD701F92B9EAB540058791B /* Alamofire */,
|
||||||
|
2AD701FB2B9EAB540058791B /* AlamofireDynamic */,
|
||||||
|
2AF729772B9F060200B4C04E /* DynamicColor */,
|
||||||
|
);
|
||||||
|
productName = comments;
|
||||||
|
productReference = 2AD701E72B9EAA970058791B /* comments.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
2AD701DF2B9EAA970058791B /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1510;
|
||||||
|
LastUpgradeCheck = 1510;
|
||||||
|
TargetAttributes = {
|
||||||
|
2AD701E62B9EAA970058791B = {
|
||||||
|
CreatedOnToolsVersion = 15.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 2AD701E22B9EAA970058791B /* Build configuration list for PBXProject "comments" */;
|
||||||
|
compatibilityVersion = "Xcode 14.0";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 2AD701DE2B9EAA970058791B;
|
||||||
|
packageReferences = (
|
||||||
|
2AD701F82B9EAB520058791B /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||||
|
2AF729762B9F060100B4C04E /* XCRemoteSwiftPackageReference "DynamicColor" */,
|
||||||
|
);
|
||||||
|
productRefGroup = 2AD701E82B9EAA970058791B /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
2AD701E62B9EAA970058791B /* comments */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
2AD701E52B9EAA970058791B /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
2AD701F22B9EAA990058791B /* Preview Assets.xcassets in Resources */,
|
||||||
|
2AD701EF2B9EAA990058791B /* Assets.xcassets in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
2AD701E32B9EAA970058791B /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
2AD701FE2B9EAFF00058791B /* Api.swift in Sources */,
|
||||||
|
2AD701ED2B9EAA970058791B /* ContentView.swift in Sources */,
|
||||||
|
2AD701EB2B9EAA970058791B /* commentsApp.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
2AD701F32B9EAA990058791B /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
2AD701F42B9EAA990058791B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
2AD701F62B9EAA990058791B /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "\"comments/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = GKND93CBMN;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = comments/Info.plist;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.ernestlitvinenko.comments;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
2AD701F72B9EAA990058791B /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_ASSET_PATHS = "\"comments/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = GKND93CBMN;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = comments/Info.plist;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.ernestlitvinenko.comments;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
2AD701E22B9EAA970058791B /* Build configuration list for PBXProject "comments" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
2AD701F32B9EAA990058791B /* Debug */,
|
||||||
|
2AD701F42B9EAA990058791B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
2AD701F52B9EAA990058791B /* Build configuration list for PBXNativeTarget "comments" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
2AD701F62B9EAA990058791B /* Debug */,
|
||||||
|
2AD701F72B9EAA990058791B /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
2AD701F82B9EAB520058791B /* XCRemoteSwiftPackageReference "Alamofire" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/Alamofire/Alamofire";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.9.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
2AF729762B9F060100B4C04E /* XCRemoteSwiftPackageReference "DynamicColor" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/yannickl/DynamicColor";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.0.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
2AD701F92B9EAB540058791B /* Alamofire */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 2AD701F82B9EAB520058791B /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
|
productName = Alamofire;
|
||||||
|
};
|
||||||
|
2AD701FB2B9EAB540058791B /* AlamofireDynamic */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 2AD701F82B9EAB520058791B /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
|
productName = AlamofireDynamic;
|
||||||
|
};
|
||||||
|
2AF729772B9F060200B4C04E /* DynamicColor */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 2AF729762B9F060100B4C04E /* XCRemoteSwiftPackageReference "DynamicColor" */;
|
||||||
|
productName = DynamicColor;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = 2AD701DF2B9EAA970058791B /* Project object */;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "alamofire",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Alamofire/Alamofire",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "723fa5a6c65812aec4a0d7cc432ee198883b6e00",
|
||||||
|
"version" : "5.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "dynamiccolor",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/yannickl/DynamicColor",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "d9beca13ca85baa50ee29832d0db5129fba69630",
|
||||||
|
"version" : "5.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Bucket
|
||||||
|
uuid = "F0643EBD-0298-429C-AEFD-39956207CF1E"
|
||||||
|
type = "1"
|
||||||
|
version = "2.0">
|
||||||
|
<Breakpoints>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "95E9ABB2-426D-4A58-8561-74AA4A12E088"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "comments/Api.swift"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "43"
|
||||||
|
endingLineNumber = "43"
|
||||||
|
landmarkName = "fetchPosts()"
|
||||||
|
landmarkType = "7">
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
</Breakpoints>
|
||||||
|
</Bucket>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>SchemeUserState</key>
|
||||||
|
<dict>
|
||||||
|
<key>Example (Playground) 1.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
</dict>
|
||||||
|
<key>Example (Playground) 2.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
</dict>
|
||||||
|
<key>Example (Playground).xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<key>comments.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
Binary file not shown.
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// Api.swift
|
||||||
|
// comments
|
||||||
|
//
|
||||||
|
// Created by Эрнест Литвиненко on 11.03.2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Alamofire
|
||||||
|
|
||||||
|
struct PostStruct: Decodable, Identifiable {
|
||||||
|
var id: Int
|
||||||
|
var userId: Int
|
||||||
|
var title: String
|
||||||
|
var body: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CardProps: Decodable, Identifiable, Hashable{
|
||||||
|
var id: Int
|
||||||
|
var name: String
|
||||||
|
var amount: Float
|
||||||
|
var lastDigits: String
|
||||||
|
var color: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FetchBalance: Decodable {
|
||||||
|
var amount: String
|
||||||
|
var currency: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Api {
|
||||||
|
private let BASE_URL = "http://localhost:3000"
|
||||||
|
func fetchPosts() async -> [PostStruct] {
|
||||||
|
let result = await AF.request(BASE_URL + "/posts").serializingDecodable([PostStruct].self).result
|
||||||
|
|
||||||
|
var data: [PostStruct] = []
|
||||||
|
|
||||||
|
do {
|
||||||
|
try data = result.get()
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchCards() async -> [CardProps] {
|
||||||
|
let result = await AF.request(BASE_URL + "/cards").serializingDecodable([CardProps].self).result
|
||||||
|
|
||||||
|
var data: [CardProps] = []
|
||||||
|
|
||||||
|
do {
|
||||||
|
try data = result.get()
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchBalance() async -> FetchBalance? {
|
||||||
|
let result = await AF.request(BASE_URL + "/balance").serializingDecodable(FetchBalance.self).result
|
||||||
|
|
||||||
|
var data: FetchBalance? = nil
|
||||||
|
|
||||||
|
do {
|
||||||
|
try data = result.get()
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x1F",
|
||||||
|
"green" : "0x1F",
|
||||||
|
"red" : "0x1E"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x1F",
|
||||||
|
"green" : "0x1F",
|
||||||
|
"red" : "0x1E"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "logo.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="72" height="22" viewBox="0 0 72 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.10074 18V5.31738H3.75504V15.8115H9.30094V18H1.10074ZM15.558 18.1934C12.6928 18.1934 10.8911 16.3564 10.8911 13.2891V13.2715C10.8911 10.2393 12.7368 8.38477 15.5493 8.38477C18.3705 8.38477 20.225 10.2217 20.225 13.2715V13.2891C20.225 16.3652 18.4057 18.1934 15.558 18.1934ZM15.5668 16.1807C16.8149 16.1807 17.6147 15.126 17.6147 13.2891V13.2715C17.6147 11.4521 16.7973 10.3975 15.5493 10.3975C14.31 10.3975 13.5014 11.4521 13.5014 13.2715V13.2891C13.5014 15.1348 14.3012 16.1807 15.5668 16.1807ZM25.4802 18.1934C23.406 18.1934 22.2282 16.8662 22.2282 14.6602V8.57812H24.7946V14.124C24.7946 15.3545 25.3747 16.0664 26.5261 16.0664C27.6775 16.0664 28.407 15.2227 28.407 13.9922V8.57812H30.9734V18H28.407V16.4443H28.3542C27.8708 17.5254 26.904 18.1934 25.4802 18.1934Z" fill="#EEFC79"/>
|
||||||
|
<path d="M33.7148 18V5.31738H38.2324C40.4648 5.31738 41.9326 6.57422 41.9326 8.44629V8.46387C41.9326 9.86133 40.9482 11.1006 39.542 11.3291V11.3555C41.335 11.5664 42.5918 12.7617 42.5918 14.5107V14.5283C42.5918 16.6816 41.0361 18 38.2236 18H33.7148ZM37.9775 6.38086H34.9102V10.9512H37.6436C39.5684 10.9512 40.7197 10.0811 40.7197 8.57812V8.56055C40.7197 7.19824 39.7002 6.38086 37.9775 6.38086ZM37.9512 11.9883H34.9102V16.9365H38.0566C40.2451 16.9365 41.3613 16.04 41.3613 14.4844V14.4668C41.3613 12.876 40.1045 11.9883 37.9512 11.9883ZM47.5569 18.167C45.7552 18.167 44.5247 17.0859 44.5247 15.4688V15.4512C44.5247 13.8955 45.7024 12.9551 47.803 12.8145L50.6507 12.6387V11.8477C50.6507 10.5293 49.8157 9.72949 48.3919 9.72949C47.0647 9.72949 46.1946 10.3711 46.0013 11.4082L45.9837 11.4961H44.8587L44.8675 11.3906C45.0432 9.81738 46.344 8.69238 48.4095 8.69238C50.5013 8.69238 51.8108 9.87012 51.8108 11.7422V18H50.6507V16.3301H50.6155C50.1145 17.4199 48.9104 18.167 47.5569 18.167ZM45.7112 15.4688C45.7112 16.4707 46.555 17.1475 47.7942 17.1475C49.3938 17.1475 50.6507 16.0225 50.6507 14.5635V13.5527L47.9436 13.7373C46.511 13.8252 45.7112 14.4492 45.7112 15.4512V15.4688ZM54.482 18V8.85938H55.6421V10.415H55.6773C56.1343 9.43066 57.1627 8.70117 58.6392 8.70117C60.6607 8.70117 61.856 10.002 61.856 12.1465V18H60.6959V12.3398C60.6959 10.6436 59.8521 9.73828 58.2964 9.73828C56.7056 9.73828 55.6421 10.8809 55.6421 12.5859V18H54.482ZM64.6502 18V5.31738H65.8104V13.166H65.8455L70.328 8.85938H71.7694L67.6649 12.7969L71.8573 18H70.4862L66.8299 13.5088L65.8104 14.458V18H64.6502Z" fill="white"/>
|
||||||
|
<path d="M44.5 2.99946C48.6667 1.49946 59.4 -0.600535 69 2.99946" stroke="#EEFC79"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "logo.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="40" height="20" viewBox="0 0 40 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.6127 5.94141L13.8848 16.0816H16.6483L18.3777 5.94141H15.6127Z" fill="#272A32"/>
|
||||||
|
<path d="M11.5697 5.95135L8.863 12.8666L8.57442 11.8225C8.04061 10.5644 6.52521 8.75755 4.74609 7.61887L7.22107 16.0752L10.1453 16.0702L14.4974 5.94922L11.5697 5.95135Z" fill="#272A32"/>
|
||||||
|
<path d="M7.52427 6.67732C7.36363 6.05965 6.89806 5.87555 6.32019 5.85352H2.03554L2 6.05538C5.33432 6.86426 7.54062 8.81325 8.45612 11.1567L7.52427 6.67732Z" fill="#272A32"/>
|
||||||
|
<path d="M23.9799 7.88378C24.8841 7.86957 25.5394 8.06717 26.0483 8.27187L26.2978 8.38916L26.6717 6.19139C26.1244 5.98597 25.2665 5.76562 24.196 5.76562C21.4652 5.76562 19.5403 7.14101 19.5254 9.11203C19.5076 10.5684 20.8965 11.3816 21.9457 11.8671C23.0225 12.3646 23.3836 12.6809 23.3786 13.1252C23.3701 13.804 22.52 14.1153 21.726 14.1153C20.6193 14.1153 20.0315 13.9625 19.1238 13.5843L18.7677 13.4223L18.3789 15.6918C19.0257 15.9754 20.2191 16.2192 21.4581 16.232C24.3631 16.232 26.2509 14.873 26.2708 12.7669C26.2829 11.6147 25.5458 10.7355 23.9487 10.014C22.982 9.54348 22.3906 9.23073 22.3963 8.75592C22.3963 8.33442 22.8981 7.88378 23.9799 7.88378Z" fill="#272A32"/>
|
||||||
|
<path d="M33.7646 5.95312H31.6294C30.9662 5.95312 30.4729 6.13367 30.1815 6.7947L26.0781 16.0876H28.9803C28.9803 16.0876 29.4537 14.838 29.561 14.5644C29.8787 14.5644 32.6984 14.5686 33.1 14.5686C33.1825 14.9226 33.437 16.0876 33.437 16.0876H36.0008L33.7646 5.95312ZM30.3564 12.4889C30.5838 11.9067 31.4574 9.65493 31.4574 9.65493C31.4425 9.68265 31.6827 9.06852 31.8256 8.68683L32.0118 9.5611C32.0118 9.5611 32.5414 11.9821 32.6522 12.4889H30.3564Z" fill="#272A32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,181 @@
|
||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// comments
|
||||||
|
//
|
||||||
|
// Created by Эрнест Литвиненко on 11.03.2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import DynamicColor
|
||||||
|
|
||||||
|
struct Theme {
|
||||||
|
public static let bg = Color(hexString: "#1E1F1F")
|
||||||
|
public static let white = Color(hexString: "#EAEAEA")
|
||||||
|
public static let skyBlue = Color(hexString: "#B2D0CE")
|
||||||
|
public static let lightYellow = Color(hexString: "#F1FE87")
|
||||||
|
public static let lightPurple = Color(hexString: "#B8A9C6")
|
||||||
|
|
||||||
|
public static let btnBg = Color(hexString: "#3E3E3E")
|
||||||
|
|
||||||
|
public static let skyGradient = LinearGradient(colors: [Theme.white, Theme.skyBlue], startPoint: UnitPoint(x: 0, y: 0), endPoint: UnitPoint(x: 0, y: 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct BalanceView: View {
|
||||||
|
@State var balance: FetchBalance? = nil
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var body: some View {
|
||||||
|
HStack{
|
||||||
|
VStack(alignment: .leading, content: {
|
||||||
|
Text("Your Balance:").foregroundStyle(.white).font(.title2)
|
||||||
|
if balance != nil {
|
||||||
|
Text("\(balance!.currency) \(balance!.amount)").foregroundStyle(.white).font(.title2).fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Text("$ 0").foregroundStyle(.white).font(.title2).fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/){
|
||||||
|
Image(systemName: "magnifyingglass").fontWeight(.bold)
|
||||||
|
}.padding().foregroundColor(.white).background(Theme.btnBg).cornerRadius(100)
|
||||||
|
|
||||||
|
}.task {
|
||||||
|
self.balance = await Api().fetchBalance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
@State var posts: [PostStruct] = []
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
ZStack{
|
||||||
|
Color.bg.ignoresSafeArea()
|
||||||
|
VStack {
|
||||||
|
Image(.logo)
|
||||||
|
BalanceView().padding(.bottom, 24)
|
||||||
|
CardsView()
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
}.padding()
|
||||||
|
}
|
||||||
|
.navigationDestination(for: CardProps.self, destination: { card in
|
||||||
|
DetailedCardView(card: card)
|
||||||
|
.navigationBarHidden(true)
|
||||||
|
})
|
||||||
|
.navigationBarHidden(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
struct CardsView: View {
|
||||||
|
@State var cardViews: [CardProps] = []
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(Axis.Set.horizontal) {
|
||||||
|
HStack{
|
||||||
|
ForEach(cardViews) { card in
|
||||||
|
CardView(cardType: card).padding(.trailing, 13)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.scrollIndicators(.hidden).task {
|
||||||
|
self.cardViews = await Api().fetchCards()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct CardView: View {
|
||||||
|
@State var cardType: CardProps
|
||||||
|
let nf = NumberFormatter()
|
||||||
|
|
||||||
|
|
||||||
|
init(cardType: CardProps) {
|
||||||
|
self.cardType = cardType
|
||||||
|
nf.numberStyle = .decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationLink(value: cardType, label:{
|
||||||
|
VStack(alignment: .leading, content: {
|
||||||
|
Image(.visa).padding(.bottom, 10)
|
||||||
|
Text(cardType.name).foregroundColor(.black)
|
||||||
|
Text("$ \(nf.string(from: cardType.amount as NSNumber)!)").padding(.bottom, 10)
|
||||||
|
Text("** \(cardType.lastDigits)").foregroundColor(.black)
|
||||||
|
})
|
||||||
|
.foregroundColor(.black)
|
||||||
|
.padding(.all, 16.0).padding(.trailing, 50)
|
||||||
|
.background(Theme.skyGradient)
|
||||||
|
.cornerRadius(20)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct LeftIconNavigation: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: {dismiss()}) {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
}.foregroundColor(.white).padding().background(Theme.btnBg).fontWeight(.bold).clipShape(.circle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DetailedCardView: View {
|
||||||
|
|
||||||
|
@State var card: CardProps
|
||||||
|
init(card: CardProps) {
|
||||||
|
self.card = card
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
ZStack{
|
||||||
|
Color.bg.ignoresSafeArea()
|
||||||
|
VStack {
|
||||||
|
HStack{
|
||||||
|
LeftIconNavigation().frame(width: 32, height: 32, alignment: .leading)
|
||||||
|
Spacer()
|
||||||
|
Text(card.name).font(.title).foregroundColor(.white).fontWeight(.bold).frame(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).padding(.leading, -32)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
HStack{
|
||||||
|
Image(.visa)
|
||||||
|
Spacer()
|
||||||
|
Text(String(format: "$ %.2f", card.amount)).font(.title3).fontWeight(.bold)
|
||||||
|
}.padding()
|
||||||
|
HStack {
|
||||||
|
Text(".... .... .... \(card.lastDigits)").font(.title3).fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
|
||||||
|
Spacer()
|
||||||
|
}.padding()
|
||||||
|
HStack {
|
||||||
|
Text("Ivanov Ivan").bold().font(.title2)
|
||||||
|
Spacer()
|
||||||
|
}.padding()
|
||||||
|
HStack{
|
||||||
|
}
|
||||||
|
}.background(Theme.skyGradient).cornerRadius(20).padding()
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
}.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
ContentView()
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="40" height="20" viewBox="0 0 40 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.6127 5.94141L13.8848 16.0816H16.6483L18.3777 5.94141H15.6127Z" fill="#272A32"/>
|
||||||
|
<path d="M11.5697 5.95135L8.863 12.8666L8.57442 11.8225C8.04061 10.5644 6.52521 8.75755 4.74609 7.61887L7.22107 16.0752L10.1453 16.0702L14.4974 5.94922L11.5697 5.95135Z" fill="#272A32"/>
|
||||||
|
<path d="M7.52427 6.67732C7.36363 6.05965 6.89806 5.87555 6.32019 5.85352H2.03554L2 6.05538C5.33432 6.86426 7.54062 8.81325 8.45612 11.1567L7.52427 6.67732Z" fill="#272A32"/>
|
||||||
|
<path d="M23.9799 7.88378C24.8841 7.86957 25.5394 8.06717 26.0483 8.27187L26.2978 8.38916L26.6717 6.19139C26.1244 5.98597 25.2665 5.76562 24.196 5.76562C21.4652 5.76562 19.5403 7.14101 19.5254 9.11203C19.5076 10.5684 20.8965 11.3816 21.9457 11.8671C23.0225 12.3646 23.3836 12.6809 23.3786 13.1252C23.3701 13.804 22.52 14.1153 21.726 14.1153C20.6193 14.1153 20.0315 13.9625 19.1238 13.5843L18.7677 13.4223L18.3789 15.6918C19.0257 15.9754 20.2191 16.2192 21.4581 16.232C24.3631 16.232 26.2509 14.873 26.2708 12.7669C26.2829 11.6147 25.5458 10.7355 23.9487 10.014C22.982 9.54348 22.3906 9.23073 22.3963 8.75592C22.3963 8.33442 22.8981 7.88378 23.9799 7.88378Z" fill="#272A32"/>
|
||||||
|
<path d="M33.7646 5.95312H31.6294C30.9662 5.95312 30.4729 6.13367 30.1815 6.7947L26.0781 16.0876H28.9803C28.9803 16.0876 29.4537 14.838 29.561 14.5644C29.8787 14.5644 32.6984 14.5686 33.1 14.5686C33.1825 14.9226 33.437 16.0876 33.437 16.0876H36.0008L33.7646 5.95312ZM30.3564 12.4889C30.5838 11.9067 31.4574 9.65493 31.4574 9.65493C31.4425 9.68265 31.6827 9.06852 31.8256 8.68683L32.0118 9.5611C32.0118 9.5611 32.5414 11.9821 32.6522 12.4889H30.3564Z" fill="#272A32"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Salary",
|
||||||
|
"amount": 100,
|
||||||
|
"lastDigits": "8907",
|
||||||
|
"color": "?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Pocket",
|
||||||
|
"amount": 100,
|
||||||
|
"lastDigits": "8907",
|
||||||
|
"color": "?"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"balance": {
|
||||||
|
"amount": "7 896",
|
||||||
|
"currency": "RUB"
|
||||||
|
},
|
||||||
|
|
||||||
|
"transactions": []
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
../json-server/lib/bin.js
|
|
@ -0,0 +1 @@
|
||||||
|
../json5/lib/cli.js
|
|
@ -0,0 +1 @@
|
||||||
|
../mime/bin/cli.js
|
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"systemParams": "darwin-arm64-115",
|
||||||
|
"modulesFolders": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"flags": [],
|
||||||
|
"linkedModules": [],
|
||||||
|
"topLevelPatterns": [
|
||||||
|
"json-server@^1.0.0-alpha.23"
|
||||||
|
],
|
||||||
|
"lockfileEntries": {
|
||||||
|
"@polka/url@^1.0.0-next.24": "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817",
|
||||||
|
"@tinyhttp/accepts@2.2.1": "https://registry.yarnpkg.com/@tinyhttp/accepts/-/accepts-2.2.1.tgz#2ea212c3108ad70aead510595e2c74577953e119",
|
||||||
|
"@tinyhttp/app@^2.2.3": "https://registry.yarnpkg.com/@tinyhttp/app/-/app-2.2.3.tgz#9336933ce84616ce43a124cd85ab360c9472ab49",
|
||||||
|
"@tinyhttp/content-disposition@2.2.0": "https://registry.yarnpkg.com/@tinyhttp/content-disposition/-/content-disposition-2.2.0.tgz#0f5b96c3262b9b774f401fa9ead172c717ff6c57",
|
||||||
|
"@tinyhttp/content-type@^0.1.4": "https://registry.yarnpkg.com/@tinyhttp/content-type/-/content-type-0.1.4.tgz#112bce3b564213e0ed43fa76fccca4237be3a634",
|
||||||
|
"@tinyhttp/cookie-signature@2.1.0": "https://registry.yarnpkg.com/@tinyhttp/cookie-signature/-/cookie-signature-2.1.0.tgz#b79c0f3b419e75dbafcefef14ad148694c97ae41",
|
||||||
|
"@tinyhttp/cookie@2.1.0": "https://registry.yarnpkg.com/@tinyhttp/cookie/-/cookie-2.1.0.tgz#aa29cb160ee4cdcbe58271353203caaf8cb2cb46",
|
||||||
|
"@tinyhttp/cors@^2.0.0": "https://registry.yarnpkg.com/@tinyhttp/cors/-/cors-2.0.0.tgz#596047e5603335ca42be1b0318f7b58d30090413",
|
||||||
|
"@tinyhttp/encode-url@2.1.1": "https://registry.yarnpkg.com/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz#a52bdbd75f541455190d1a16b0a81374c8dc587d",
|
||||||
|
"@tinyhttp/etag@2.1.1": "https://registry.yarnpkg.com/@tinyhttp/etag/-/etag-2.1.1.tgz#0813babee4b3e2c177b0d7169e1ef309a85a9b11",
|
||||||
|
"@tinyhttp/forwarded@2.1.2": "https://registry.yarnpkg.com/@tinyhttp/forwarded/-/forwarded-2.1.2.tgz#24762f8d422e62553b101f19a2c1c68147ca10fd",
|
||||||
|
"@tinyhttp/proxy-addr@2.1.3": "https://registry.yarnpkg.com/@tinyhttp/proxy-addr/-/proxy-addr-2.1.3.tgz#27f13048cd2a0800496d8353737a7a2ae4bcf6cd",
|
||||||
|
"@tinyhttp/req@2.2.2": "https://registry.yarnpkg.com/@tinyhttp/req/-/req-2.2.2.tgz#f1f4c233bb4bee587808624644389b74464e9371",
|
||||||
|
"@tinyhttp/res@2.2.2": "https://registry.yarnpkg.com/@tinyhttp/res/-/res-2.2.2.tgz#4c7f4f326e52320e2ad251b1082e58d6fb1e7f0b",
|
||||||
|
"@tinyhttp/router@2.2.2": "https://registry.yarnpkg.com/@tinyhttp/router/-/router-2.2.2.tgz#f2cca8b880a9ae6b184711542f6958dafbb8e30e",
|
||||||
|
"@tinyhttp/send@2.2.1": "https://registry.yarnpkg.com/@tinyhttp/send/-/send-2.2.1.tgz#62814ad8b8ba0c65ba0d557db0d444a6a2e852f2",
|
||||||
|
"@tinyhttp/type-is@2.2.2": "https://registry.yarnpkg.com/@tinyhttp/type-is/-/type-is-2.2.2.tgz#8699a5f7cc43a9da719586840cb4ccb067b8dd58",
|
||||||
|
"@tinyhttp/url@2.1.1": "https://registry.yarnpkg.com/@tinyhttp/url/-/url-2.1.1.tgz#77fa8963f5b698bacbdc6912407f946d32c793e1",
|
||||||
|
"@tinyhttp/vary@^0.1.3": "https://registry.yarnpkg.com/@tinyhttp/vary/-/vary-0.1.3.tgz#f5bea4769f380c43a158832a8daad8e8b186757c",
|
||||||
|
"anymatch@~3.1.2": "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e",
|
||||||
|
"binary-extensions@^2.0.0": "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d",
|
||||||
|
"braces@~3.0.2": "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107",
|
||||||
|
"chalk@^5.3.0": "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385",
|
||||||
|
"chokidar@^3.5.3": "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b",
|
||||||
|
"dot-prop@^8.0.2": "https://registry.yarnpkg.com/dot-prop/-/dot-prop-8.0.2.tgz#afda6866610684dd155a96538f8efcdf78a27f18",
|
||||||
|
"es-escape-html@^0.1.1": "https://registry.yarnpkg.com/es-escape-html/-/es-escape-html-0.1.1.tgz#9a582d49754ec6204524952c76a383fe5f03c1c0",
|
||||||
|
"es-vary@^0.1.1": "https://registry.yarnpkg.com/es-vary/-/es-vary-0.1.2.tgz#d02940618962e2ffbdde9eedb876bec4c9e06f6c",
|
||||||
|
"eta@^3.2.0": "https://registry.yarnpkg.com/eta/-/eta-3.2.0.tgz#7a2af4c897f57ea9483f5d953dc35c1ab0e97080",
|
||||||
|
"fill-range@^7.0.1": "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40",
|
||||||
|
"fsevents@~2.3.2": "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6",
|
||||||
|
"glob-parent@~5.1.2": "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4",
|
||||||
|
"header-range-parser@1.1.3": "https://registry.yarnpkg.com/header-range-parser/-/header-range-parser-1.1.3.tgz#6414b5b12e3b645d29d85225a58fd207d66d30ef",
|
||||||
|
"header-range-parser@^1.1.3": "https://registry.yarnpkg.com/header-range-parser/-/header-range-parser-1.1.3.tgz#6414b5b12e3b645d29d85225a58fd207d66d30ef",
|
||||||
|
"inflection@^3.0.0": "https://registry.yarnpkg.com/inflection/-/inflection-3.0.0.tgz#6a956fa90d72a27d22e6b32ec1064877593ee23b",
|
||||||
|
"ipaddr.js@^2.1.0": "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f",
|
||||||
|
"is-binary-path@~2.1.0": "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09",
|
||||||
|
"is-extglob@^2.1.1": "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2",
|
||||||
|
"is-glob@^4.0.1": "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084",
|
||||||
|
"is-glob@~4.0.1": "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084",
|
||||||
|
"is-number@^7.0.0": "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b",
|
||||||
|
"json-server@^1.0.0-alpha.23": "https://registry.yarnpkg.com/json-server/-/json-server-1.0.0-alpha.23.tgz#16ce3c1bf51b36e5770e4d4626687be916e4dac1",
|
||||||
|
"json5@^2.2.3": "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283",
|
||||||
|
"lowdb@^7.0.1": "https://registry.yarnpkg.com/lowdb/-/lowdb-7.0.1.tgz#7354a684547d76206b1c730b9434604235b125e5",
|
||||||
|
"milliparsec@^2.3.0": "https://registry.yarnpkg.com/milliparsec/-/milliparsec-2.3.0.tgz#4644a08c2e700ff7d55881f4054aab270c89cb6f",
|
||||||
|
"mime@4.0.1": "https://registry.yarnpkg.com/mime/-/mime-4.0.1.tgz#ad7563d1bfe30253ad97dedfae2b1009d01b9470",
|
||||||
|
"mrmime@^2.0.0": "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4",
|
||||||
|
"negotiator@^0.6.3": "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd",
|
||||||
|
"normalize-path@^3.0.0": "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65",
|
||||||
|
"normalize-path@~3.0.0": "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65",
|
||||||
|
"picomatch@^2.0.4": "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42",
|
||||||
|
"picomatch@^2.2.1": "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42",
|
||||||
|
"readdirp@~3.6.0": "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7",
|
||||||
|
"regexparam@^2.0.1": "https://registry.yarnpkg.com/regexparam/-/regexparam-2.0.2.tgz#a0f6aa057c67b1c9c09508c45823c0755b1f6e58",
|
||||||
|
"sirv@^2.0.4": "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0",
|
||||||
|
"sort-on@^6.0.0": "https://registry.yarnpkg.com/sort-on/-/sort-on-6.0.0.tgz#3aeb0a939a81f7ca44d257f40b8e55be4788a3f4",
|
||||||
|
"steno@^4.0.2": "https://registry.yarnpkg.com/steno/-/steno-4.0.2.tgz#9bd9b0ffc226a1f9436f29132c8b8e7199d22c50",
|
||||||
|
"to-regex-range@^5.0.1": "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4",
|
||||||
|
"totalist@^3.0.0": "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8",
|
||||||
|
"type-fest@^3.8.0": "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"artifacts": {}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
const qs = require('querystring');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef ParsedURL
|
||||||
|
* @type {import('.').ParsedURL}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Request
|
||||||
|
* @property {string} url
|
||||||
|
* @property {ParsedURL} _parsedUrl
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Request} req
|
||||||
|
* @returns {ParsedURL|void}
|
||||||
|
*/
|
||||||
|
function parse(req) {
|
||||||
|
let raw = req.url;
|
||||||
|
if (raw == null) return;
|
||||||
|
|
||||||
|
let prev = req._parsedUrl;
|
||||||
|
if (prev && prev.raw === raw) return prev;
|
||||||
|
|
||||||
|
let pathname=raw, search='', query;
|
||||||
|
|
||||||
|
if (raw.length > 1) {
|
||||||
|
let idx = raw.indexOf('?', 1);
|
||||||
|
|
||||||
|
if (idx !== -1) {
|
||||||
|
search = raw.substring(idx);
|
||||||
|
pathname = raw.substring(0, idx);
|
||||||
|
if (search.length > 1) {
|
||||||
|
query = qs.parse(search.substring(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return req._parsedUrl = { pathname, search, query, raw };
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.parse = parse;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import * as qs from 'querystring';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef ParsedURL
|
||||||
|
* @type {import('.').ParsedURL}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Request
|
||||||
|
* @property {string} url
|
||||||
|
* @property {ParsedURL} _parsedUrl
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Request} req
|
||||||
|
* @returns {ParsedURL|void}
|
||||||
|
*/
|
||||||
|
export function parse(req) {
|
||||||
|
let raw = req.url;
|
||||||
|
if (raw == null) return;
|
||||||
|
|
||||||
|
let prev = req._parsedUrl;
|
||||||
|
if (prev && prev.raw === raw) return prev;
|
||||||
|
|
||||||
|
let pathname=raw, search='', query;
|
||||||
|
|
||||||
|
if (raw.length > 1) {
|
||||||
|
let idx = raw.indexOf('?', 1);
|
||||||
|
|
||||||
|
if (idx !== -1) {
|
||||||
|
search = raw.substring(idx);
|
||||||
|
pathname = raw.substring(0, idx);
|
||||||
|
if (search.length > 1) {
|
||||||
|
query = qs.parse(search.substring(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return req._parsedUrl = { pathname, search, query, raw };
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import type { IncomingMessage } from 'http';
|
||||||
|
|
||||||
|
export interface ParsedURL {
|
||||||
|
pathname: string;
|
||||||
|
search: string;
|
||||||
|
query: Record<string, string | string[]> | void;
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(req: IncomingMessage): ParsedURL;
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-next.25",
|
||||||
|
"name": "@polka/url",
|
||||||
|
"repository": "lukeed/polka",
|
||||||
|
"description": "Super fast, memoized `req.url` parser",
|
||||||
|
"module": "build.mjs",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"main": "build.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"import": "./build.mjs",
|
||||||
|
"require": "./build.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"build.*",
|
||||||
|
"index.d.*"
|
||||||
|
],
|
||||||
|
"author": {
|
||||||
|
"name": "Luke Edwards",
|
||||||
|
"email": "luke@lukeed.com",
|
||||||
|
"url": "https://lukeed.com"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
# @polka/url [](https://npmjs.org/package/@polka/url) [](https://licenses.dev/npm/%40polka%2Furl)
|
||||||
|
|
||||||
|
> Super fast, memoized `req.url` parser; _not_ limited to [Polka][polka]!
|
||||||
|
|
||||||
|
Parses the `url` from a [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) request. The returned object will always only contain the following keys: `search`, `query`, `pathname`, and `raw`.
|
||||||
|
|
||||||
|
> **Note:** This library does not process `protocol`, `hostname`, `port`, etc.<br>This is because the incoming `req.url` value only begins with the path information.
|
||||||
|
|
||||||
|
Parsed requests will be mutated with a `_parsedUrl` key, containing the returned output. This is used for future memoization, avoiding the need to fully parse the same `url` value multiple times.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install --save @polka/url
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
const parse = require('@polka/url');
|
||||||
|
|
||||||
|
let req = {
|
||||||
|
url: '/foo/bar?fizz=buzz'
|
||||||
|
};
|
||||||
|
let output = parse(req);
|
||||||
|
//=> {
|
||||||
|
//=> pathname: '/foo/bar',
|
||||||
|
//=> raw: '/foo/bar?fizz=buzz',
|
||||||
|
//=> search: '?fizz=buzz',
|
||||||
|
//=> query: {
|
||||||
|
//=> fizz: 'buzz'
|
||||||
|
//=> },
|
||||||
|
//=> }
|
||||||
|
|
||||||
|
// Attaches result for future memoization
|
||||||
|
assert.deepEqual(output, req._parsedUrl); //=> true
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### url(req)
|
||||||
|
Returns: `Object` or `undefined`
|
||||||
|
|
||||||
|
> **Important:** The `req` must have a `url` key, otherwise `undefined` will be returned.<br>If no input is provided at all, a `TypeError` will be thrown.
|
||||||
|
|
||||||
|
#### req
|
||||||
|
Type: `IncomingMessage` or `{ url: string }`
|
||||||
|
|
||||||
|
The incoming HTTP request (`req`) or a plain `Object` with a `url` key.
|
||||||
|
|
||||||
|
> **Note:** In Node.js servers, the [`req.url`](https://nodejs.org/api/http.html#http_message_url) begins with a pathname & does not include a `hash`.
|
||||||
|
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Check out the [`bench`](/bench) directory for in-depth benchmark results and comparisons.
|
||||||
|
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Any issues or questions can be sent to the [Polka][polka] repository.<br>However, please specify that your inquiry is about `@polka/url` specifically.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Luke Edwards](https://lukeed.com)
|
||||||
|
|
||||||
|
[polka]: https://github.com/lukeed/polka
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,117 @@
|
||||||
|
# @tinyhttp/accepts
|
||||||
|
|
||||||
|
> [`accepts`](https://github.com/jshttp/accepts) rewrite in TypeScript.
|
||||||
|
|
||||||
|
Higher level content negotiation based on
|
||||||
|
[negotiator](https://www.npmjs.com/package/negotiator). Extracted from
|
||||||
|
[koa](https://www.npmjs.com/package/koa) for general use.
|
||||||
|
|
||||||
|
In addition to negotiator, it allows:
|
||||||
|
|
||||||
|
- Allows types as an array or arguments list, ie
|
||||||
|
`(['text/html', 'application/json'])` as well as
|
||||||
|
`('text/html', 'application/json')`.
|
||||||
|
- Allows type shorthands such as `json`.
|
||||||
|
- Returns `false` when no types match
|
||||||
|
- Treats non-existent headers as `*`
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/accepts
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Accepts } from '@tinyhttp/accepts'
|
||||||
|
```
|
||||||
|
|
||||||
|
### accepts(req)
|
||||||
|
|
||||||
|
Create a new `Accepts` object for the given `req`.
|
||||||
|
|
||||||
|
#### `.charset(charsets)`
|
||||||
|
|
||||||
|
Return the first accepted charset. If nothing in `charsets` is accepted, then
|
||||||
|
`false` is returned.
|
||||||
|
|
||||||
|
#### `.charsets()`
|
||||||
|
|
||||||
|
Return the charsets that the request accepts, in the order of the client's
|
||||||
|
preference (most preferred first).
|
||||||
|
|
||||||
|
#### `.encoding(encodings)`
|
||||||
|
|
||||||
|
Return the first accepted encoding. If nothing in `encodings` is accepted, then
|
||||||
|
`false` is returned.
|
||||||
|
|
||||||
|
#### `.encodings()`
|
||||||
|
|
||||||
|
Return the encodings that the request accepts, in the order of the client's
|
||||||
|
preference (most preferred first).
|
||||||
|
|
||||||
|
#### `.language(languages)`
|
||||||
|
|
||||||
|
Return the first accepted language. If nothing in `languages` is accepted, then
|
||||||
|
`false` is returned.
|
||||||
|
|
||||||
|
#### `.languages()`
|
||||||
|
|
||||||
|
Return the languages that the request accepts, in the order of the client's
|
||||||
|
preference (most preferred first).
|
||||||
|
|
||||||
|
#### `.type(types)`
|
||||||
|
|
||||||
|
Return the first accepted type (and it is returned as the same text as what
|
||||||
|
appears in the `types` array). If nothing in `types` is accepted, then `false`
|
||||||
|
is returned.
|
||||||
|
|
||||||
|
The `types` array can contain full MIME types or file extensions. Any value that
|
||||||
|
is not a full MIME types is passed to `require('mime-types').lookup`.
|
||||||
|
|
||||||
|
#### `.types()`
|
||||||
|
|
||||||
|
Return the types that the request accepts, in the order of the client's
|
||||||
|
preference (most preferred first).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This simple example shows how to use `accepts` to return a different typed
|
||||||
|
respond body based on what the client wants to accept. The server lists it's
|
||||||
|
preferences in order and will get back the best match between the client and
|
||||||
|
server.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import Accepts from '@tinyhttp/accepts'
|
||||||
|
import { createServer } from 'node:http'
|
||||||
|
|
||||||
|
createServer((req, res) => {
|
||||||
|
const accept = new Accepts(req)
|
||||||
|
|
||||||
|
// the order of this list is significant; should be server preferred order
|
||||||
|
switch (accept.type(['json', 'html'])) {
|
||||||
|
case 'json':
|
||||||
|
res.setHeader('Content-Type', 'application/json')
|
||||||
|
res.write('{"hello":"world!"}')
|
||||||
|
break
|
||||||
|
case 'html':
|
||||||
|
res.setHeader('Content-Type', 'text/html')
|
||||||
|
res.write('<b>hello, world!</b>')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
// the fallback is text/plain, so no need to specify it above
|
||||||
|
res.setHeader('Content-Type', 'text/plain')
|
||||||
|
res.write('hello, world!')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
res.end()
|
||||||
|
}).listen(3000)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can test this out with the cURL program:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -I -H 'Accept: text/html' http://localhost:3000/
|
||||||
|
```
|
|
@ -0,0 +1,49 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
import Negotiator from 'negotiator';
|
||||||
|
import { IncomingMessage as I, IncomingHttpHeaders } from 'node:http';
|
||||||
|
export declare class Accepts {
|
||||||
|
headers: IncomingHttpHeaders;
|
||||||
|
negotiator: Negotiator;
|
||||||
|
constructor(req: Pick<I, 'headers'>);
|
||||||
|
/**
|
||||||
|
* Check if the given `type(s)` is acceptable, returning the best match when true, otherwise `false`, in which case you should respond with 406 "Not Acceptable".
|
||||||
|
*
|
||||||
|
* The `type` value may be a single mime type string such as "application/json", the extension name such as "json" or an array `["json", "html", "text/plain"]`. When a list or array is given the _best_ match, if any is returned. When no types are given as arguments, returns all types accepted by the client in the preference order.
|
||||||
|
*/
|
||||||
|
types(types: string | string[], ...args: string[]): string[] | string | false;
|
||||||
|
get type(): (types: string | string[], ...args: string[]) => string[] | string | false;
|
||||||
|
/**
|
||||||
|
* Return accepted encodings or best fit based on `encodings`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Encoding: gzip, deflate`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['gzip', 'deflate']
|
||||||
|
*/
|
||||||
|
encodings(encodings: string | string[], ...args: string[]): string | string[] | boolean;
|
||||||
|
get encoding(): (encodings: string | string[], ...args: string[]) => string | string[] | boolean;
|
||||||
|
/**
|
||||||
|
* Return accepted charsets or best fit based on `charsets`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['utf-8', 'utf-7', 'iso-8859-1']
|
||||||
|
*/
|
||||||
|
charsets(charsets?: string | string[], ...args: string[]): string | string[] | boolean;
|
||||||
|
get charset(): (charsets: string | string[], ...args: string[]) => string | string[] | boolean;
|
||||||
|
/**
|
||||||
|
* Return accepted languages or best fit based on `langs`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Language: en;q=0.8, es, pt`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['es', 'pt', 'en']
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
languages(languages: string | string[], ...args: string[]): string | string[] | boolean;
|
||||||
|
get lang(): (languages: string | string[], ...args: string[]) => string | string[] | boolean;
|
||||||
|
get langs(): (languages: string | string[], ...args: string[]) => string | string[] | boolean;
|
||||||
|
get language(): (languages: string | string[], ...args: string[]) => string | string[] | boolean;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,eAAe,IAAI,CAAC,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAOrE,qBAAa,OAAO;IAClB,OAAO,EAAE,mBAAmB,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;gBACV,GAAG,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC;IAInC;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK;IA0B7E,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAErF;IACD;;;;;;;OAOG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAevF,IAAI,QAAQ,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAE/F;IACD;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAetF,IAAI,OAAO,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAE7F;IACD;;;;;;;;OAQG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAevF,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAE3F;IACD,IAAI,KAAK,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAE5F;IACD,IAAI,QAAQ,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAE/F;CACF"}
|
|
@ -0,0 +1,110 @@
|
||||||
|
import Negotiator from "negotiator";
|
||||||
|
import mime from "mime";
|
||||||
|
const extToMime = (type) => type.indexOf("/") == -1 ? mime.getType(type) : type;
|
||||||
|
const validMime = (type) => typeof type == "string";
|
||||||
|
class Accepts {
|
||||||
|
constructor(req) {
|
||||||
|
this.headers = req.headers;
|
||||||
|
this.negotiator = new Negotiator(req);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if the given `type(s)` is acceptable, returning the best match when true, otherwise `false`, in which case you should respond with 406 "Not Acceptable".
|
||||||
|
*
|
||||||
|
* The `type` value may be a single mime type string such as "application/json", the extension name such as "json" or an array `["json", "html", "text/plain"]`. When a list or array is given the _best_ match, if any is returned. When no types are given as arguments, returns all types accepted by the client in the preference order.
|
||||||
|
*/
|
||||||
|
types(types, ...args) {
|
||||||
|
let mimeTypes = [];
|
||||||
|
if (types && !Array.isArray(types)) {
|
||||||
|
mimeTypes = [types, ...args];
|
||||||
|
} else if (types) {
|
||||||
|
mimeTypes = [...types, ...args];
|
||||||
|
}
|
||||||
|
if (!mimeTypes || mimeTypes.length == 0) {
|
||||||
|
return this.negotiator.mediaTypes();
|
||||||
|
}
|
||||||
|
if (!this.headers["accept"]) {
|
||||||
|
return mimeTypes[0];
|
||||||
|
}
|
||||||
|
const mimes = mimeTypes.map(extToMime);
|
||||||
|
const accepts = this.negotiator.mediaTypes(mimes.filter(validMime));
|
||||||
|
const [first] = accepts;
|
||||||
|
return first ? mimeTypes[mimes.indexOf(first)] : false;
|
||||||
|
}
|
||||||
|
get type() {
|
||||||
|
return this.types;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return accepted encodings or best fit based on `encodings`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Encoding: gzip, deflate`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['gzip', 'deflate']
|
||||||
|
*/
|
||||||
|
encodings(encodings, ...args) {
|
||||||
|
let _encodings = encodings;
|
||||||
|
if (_encodings && !Array.isArray(_encodings)) {
|
||||||
|
_encodings = [_encodings, ...args];
|
||||||
|
}
|
||||||
|
if (!_encodings || _encodings.length == 0) {
|
||||||
|
return this.negotiator.encodings();
|
||||||
|
}
|
||||||
|
return this.negotiator.encodings(_encodings)[0] || false;
|
||||||
|
}
|
||||||
|
get encoding() {
|
||||||
|
return this.encodings;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return accepted charsets or best fit based on `charsets`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['utf-8', 'utf-7', 'iso-8859-1']
|
||||||
|
*/
|
||||||
|
charsets(charsets, ...args) {
|
||||||
|
let _charsets = charsets;
|
||||||
|
if (_charsets && !Array.isArray(_charsets)) {
|
||||||
|
_charsets = [_charsets, ...args];
|
||||||
|
}
|
||||||
|
if (!_charsets || _charsets.length == 0) {
|
||||||
|
return this.negotiator.charsets();
|
||||||
|
}
|
||||||
|
return this.negotiator.charsets(_charsets)[0] || false;
|
||||||
|
}
|
||||||
|
get charset() {
|
||||||
|
return this.charsets;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return accepted languages or best fit based on `langs`.
|
||||||
|
*
|
||||||
|
* Given `Accept-Language: en;q=0.8, es, pt`
|
||||||
|
* an array sorted by quality is returned:
|
||||||
|
*
|
||||||
|
* ['es', 'pt', 'en']
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
languages(languages, ...args) {
|
||||||
|
let _languages = languages;
|
||||||
|
if (_languages && !Array.isArray(_languages)) {
|
||||||
|
_languages = [_languages, ...args];
|
||||||
|
}
|
||||||
|
if (!_languages || _languages.length == 0) {
|
||||||
|
return this.negotiator.languages();
|
||||||
|
}
|
||||||
|
return this.negotiator.languages(_languages)[0] || false;
|
||||||
|
}
|
||||||
|
get lang() {
|
||||||
|
return this.languages;
|
||||||
|
}
|
||||||
|
get langs() {
|
||||||
|
return this.languages;
|
||||||
|
}
|
||||||
|
get language() {
|
||||||
|
return this.languages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
Accepts
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
1
comments/Home/node_modules/@tinyhttp/accepts/node_modules/.bin/mime
generated
vendored
Symbolic link
1
comments/Home/node_modules/@tinyhttp/accepts/node_modules/.bin/mime
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../../mime/bin/cli.js
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/accepts",
|
||||||
|
"description": "accepts rewrite in TypeScript",
|
||||||
|
"version": "2.2.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://tinyhttp.v1rtl.site",
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/cookie-signature"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/negotiator": "^0.6.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mime": "4.0.1",
|
||||||
|
"negotiator": "^0.6.3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,25 @@
|
||||||
|
# @tinyhttp/app
|
||||||
|
|
||||||
|
The core of tinyhttp. Contains the `App`, `Request` and `Response`. Additionally, it provides special tinyhttp-specific types.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { App } from '@tinyhttp/app'
|
||||||
|
import type { Request, Response, NextFunction } from '@tinyhttp/app'
|
||||||
|
|
||||||
|
new App()
|
||||||
|
.use((req: Request, res: Response, next: NextFunction) => {
|
||||||
|
console.log('Did a request')
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
.get('/', (_, res) => res.send('<h1>Hello World</h1>'))
|
||||||
|
.get('/page/:page', (req, res) => res.send(`You opened ${req.params.page}`))
|
||||||
|
.listen(3000)
|
||||||
|
```
|
|
@ -0,0 +1,106 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { Server } from 'node:http';
|
||||||
|
import type { Request } from './request.js';
|
||||||
|
import type { Response } from './response.js';
|
||||||
|
import type { ErrorHandler } from './onError.js';
|
||||||
|
import type { Middleware, Handler, NextFunction, UseMethodParams } from '@tinyhttp/router';
|
||||||
|
import { Router } from '@tinyhttp/router';
|
||||||
|
import { AppConstructor, AppRenderOptions, AppSettings, TemplateEngine } from './types.js';
|
||||||
|
import { TemplateEngineOptions } from './index.js';
|
||||||
|
/**
|
||||||
|
* `App` class - the starting point of tinyhttp app.
|
||||||
|
*
|
||||||
|
* With the `App` you can:
|
||||||
|
* * use routing methods and `.use(...)`
|
||||||
|
* * set no match (404) and error (500) handlers
|
||||||
|
* * configure template engines
|
||||||
|
* * store data in locals
|
||||||
|
* * listen the http server on a specified port
|
||||||
|
*
|
||||||
|
* In case you use TypeScript, you can pass custom types to this class because it is also a generic class.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* interface CoolReq extends Request {
|
||||||
|
* genericsAreDope: boolean
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* const app = App<any, CoolReq, Response>()
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export declare class App<Req extends Request = Request, Res extends Response = Response> extends Router<App, Req, Res> {
|
||||||
|
#private;
|
||||||
|
middleware: Middleware<Req, Res>[];
|
||||||
|
locals: Record<string, unknown>;
|
||||||
|
noMatchHandler: Handler;
|
||||||
|
onError: ErrorHandler;
|
||||||
|
settings: AppSettings;
|
||||||
|
engines: Record<string, TemplateEngine>;
|
||||||
|
applyExtensions: (req: Request, res: Response, next: NextFunction) => void;
|
||||||
|
attach: (req: Req, res: Res) => void;
|
||||||
|
cache: Record<string, unknown>;
|
||||||
|
constructor(options?: AppConstructor<Req, Res>);
|
||||||
|
/**
|
||||||
|
* Set app setting
|
||||||
|
* @param setting setting name
|
||||||
|
* @param value setting value
|
||||||
|
*/
|
||||||
|
set<K extends keyof AppSettings>(setting: K, value: AppSettings[K]): this;
|
||||||
|
/**
|
||||||
|
* Enable app setting
|
||||||
|
* @param setting Setting name
|
||||||
|
*/
|
||||||
|
enable<K extends keyof AppSettings>(setting: K): this;
|
||||||
|
/**
|
||||||
|
* Check if setting is enabled
|
||||||
|
* @param setting Setting name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
enabled<K extends keyof AppSettings>(setting: K): boolean;
|
||||||
|
/**
|
||||||
|
* Disable app setting
|
||||||
|
* @param setting Setting name
|
||||||
|
*/
|
||||||
|
disable<K extends keyof AppSettings>(setting: K): this;
|
||||||
|
/**
|
||||||
|
* Return the app's absolute pathname
|
||||||
|
* based on the parent(s) that have
|
||||||
|
* mounted it.
|
||||||
|
*
|
||||||
|
* For example if the application was
|
||||||
|
* mounted as `"/admin"`, which itself
|
||||||
|
* was mounted as `"/blog"` then the
|
||||||
|
* return value would be `"/blog/admin"`.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
path(): string;
|
||||||
|
/**
|
||||||
|
* Register a template engine with extension
|
||||||
|
*/
|
||||||
|
engine<RenderOptions extends TemplateEngineOptions = TemplateEngineOptions>(ext: string, fn: TemplateEngine<RenderOptions>): this;
|
||||||
|
/**
|
||||||
|
* Render a template
|
||||||
|
* @param file What to render
|
||||||
|
* @param data data that is passed to a template
|
||||||
|
* @param options Template engine options
|
||||||
|
* @param cb Callback that consumes error and html
|
||||||
|
*/
|
||||||
|
render<RenderOptions extends TemplateEngineOptions = TemplateEngineOptions>(name: string, data: Record<string, unknown>, options: AppRenderOptions<RenderOptions>, cb: (err: unknown, html?: unknown) => void): void;
|
||||||
|
use(...args: UseMethodParams<Req, Res, App>): this;
|
||||||
|
route(path: string): App;
|
||||||
|
/**
|
||||||
|
* Extends Req / Res objects, pushes 404 and 500 handlers, dispatches middleware
|
||||||
|
* @param req Req object
|
||||||
|
* @param res Res object
|
||||||
|
*/
|
||||||
|
handler<RenderOptions extends TemplateEngineOptions = TemplateEngineOptions>(req: Req, res: Res, next?: NextFunction): void;
|
||||||
|
/**
|
||||||
|
* Creates HTTP server and dispatches middleware
|
||||||
|
* @param port server listening port
|
||||||
|
* @param Server callback after server starts listening
|
||||||
|
* @param host server listening host
|
||||||
|
*/
|
||||||
|
listen(port?: number, cb?: () => void, host?: string): Server;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=app.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAA;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC1F,OAAO,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAA;AAIzD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAuBlD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,GAAG,CAAC,GAAG,SAAS,OAAO,GAAG,OAAO,EAAE,GAAG,SAAS,QAAQ,GAAG,QAAQ,CAAE,SAAQ,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;;IAC5G,UAAU,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAK;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAK;IACpC,cAAc,EAAE,OAAO,CAAA;IACvB,OAAO,EAAE,YAAY,CAAA;IACrB,QAAQ,EAAE,WAAW,CAAA;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAK;IAC5C,eAAe,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;IAC1E,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAElB,OAAO,GAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAM;IAgBlD;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAMzE;;;OAGG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI;IAMrD;;;;OAIG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO;IAIzD;;;OAGG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI;IAMtD;;;;;;;;;;OAUG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,MAAM,CAAC,aAAa,SAAS,qBAAqB,GAAG,qBAAqB,EACxE,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,cAAc,CAAC,aAAa,CAAC,GAChC,IAAI;IAMP;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,SAAS,qBAAqB,GAAG,qBAAqB,EACxE,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,OAAO,EAAE,gBAAgB,CAAC,aAAa,CAAyC,EAChF,EAAE,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,GACzC,IAAI;IA8CP,GAAG,CAAC,GAAG,IAAI,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI;IAiElD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IAsBxB;;;;OAIG;IACH,OAAO,CAAC,aAAa,SAAS,qBAAqB,GAAG,qBAAqB,EACzE,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,IAAI,CAAC,EAAE,YAAY,GAClB,IAAI;IAyFP;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;CAG9D"}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Request } from './request.js';
|
||||||
|
import type { NextFunction } from '@tinyhttp/router';
|
||||||
|
import type { Response } from './response.js';
|
||||||
|
import { App } from './app.js';
|
||||||
|
import { TemplateEngineOptions } from './types.js';
|
||||||
|
/**
|
||||||
|
* Extends Request and Response objects with custom properties and methods
|
||||||
|
*/
|
||||||
|
export declare const extendMiddleware: <EngineOptions extends TemplateEngineOptions = TemplateEngineOptions>(app: App) => (req: Request, res: Response<EngineOptions>, next: NextFunction) => void;
|
||||||
|
//# sourceMappingURL=extend.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"extend.d.ts","sourceRoot":"","sources":["../src/extend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,OAAO,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAkC7C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD;;GAEG;AACH,eAAO,MAAM,gBAAgB,6EACgD,GAAG,WACxE,OAAO,sCAAsC,YAAY,KAAG,IAsDjE,CAAA"}
|
|
@ -0,0 +1,15 @@
|
||||||
|
export { App } from './app.js';
|
||||||
|
export * from './request.js';
|
||||||
|
export * from './response.js';
|
||||||
|
export { extendMiddleware } from './extend.js';
|
||||||
|
export { onErrorHandler, type ErrorHandler } from './onError.js';
|
||||||
|
export { View } from './view.js';
|
||||||
|
export type { AppSettings, TemplateEngineOptions, TemplateEngine, AppConstructor } from './types.js';
|
||||||
|
import type { Request } from './request.js';
|
||||||
|
import type { Response } from './response.js';
|
||||||
|
import type { NextFunction, Handler as RHandler, AsyncHandler as RAsyncHandler, SyncHandler as RSyncHandler, Middleware } from '@tinyhttp/router';
|
||||||
|
export type Handler = RHandler<Request, Response>;
|
||||||
|
export type AsyncHandler = RAsyncHandler<Request, Response>;
|
||||||
|
export type SyncHandler = RSyncHandler<Request, Response>;
|
||||||
|
export type { NextFunction, Middleware, Request, Response };
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEpG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,IAAI,QAAQ,EACnB,YAAY,IAAI,aAAa,EAC7B,WAAW,IAAI,YAAY,EAC3B,UAAU,EACX,MAAM,kBAAkB,CAAA;AAEzB,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AACjD,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AAC3D,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA"}
|
|
@ -0,0 +1,464 @@
|
||||||
|
import { STATUS_CODES, createServer } from "node:http";
|
||||||
|
import { proxyaddr, all, compile } from "@tinyhttp/proxy-addr";
|
||||||
|
import { isIP } from "node:net";
|
||||||
|
import { getRequestHeader, getQueryParams, getRangeFromHeader, getAccepts, getAcceptsCharsets, getAcceptsEncodings, getAcceptsLanguages, checkIfXMLHttpRequest, getFreshOrStale, getPathname, getURLParams } from "@tinyhttp/req";
|
||||||
|
import { getURLParams as getURLParams2 } from "@tinyhttp/req";
|
||||||
|
import { Router, pushMiddleware } from "@tinyhttp/router";
|
||||||
|
import { getResponseHeader, setHeader, send, json, status, sendStatus, sendFile, setContentType, setLocationHeader, setLinksHeader, setVaryHeader, setCookie, clearCookie, formatResponse, redirect, attachment, download, append } from "@tinyhttp/res";
|
||||||
|
import { parse } from "regexparam";
|
||||||
|
import { extname, resolve, dirname, basename, join } from "node:path";
|
||||||
|
import { statSync } from "node:fs";
|
||||||
|
const trustRemoteAddress = ({ socket }) => {
|
||||||
|
const val = socket.remoteAddress;
|
||||||
|
if (typeof val === "string")
|
||||||
|
return compile(val.split(",").map((x) => x.trim()));
|
||||||
|
return compile(val || []);
|
||||||
|
};
|
||||||
|
const getProtocol = (req) => {
|
||||||
|
const proto = `http${req.secure ? "s" : ""}`;
|
||||||
|
if (!trustRemoteAddress(req))
|
||||||
|
return proto;
|
||||||
|
const header = req.headers["X-Forwarded-Proto"] || proto;
|
||||||
|
const index = header.indexOf(",");
|
||||||
|
return index !== -1 ? header.substring(0, index).trim() : header.trim();
|
||||||
|
};
|
||||||
|
const getHostname = (req) => {
|
||||||
|
let host = req.get("X-Forwarded-Host");
|
||||||
|
if (!host || !trustRemoteAddress(req))
|
||||||
|
host = req.get("Host");
|
||||||
|
if (!host)
|
||||||
|
return;
|
||||||
|
const index = host.indexOf(":", host[0] === "[" ? host.indexOf("]") + 1 : 0);
|
||||||
|
return index !== -1 ? host.substring(0, index) : host;
|
||||||
|
};
|
||||||
|
const getIP = (req) => proxyaddr(req, trustRemoteAddress(req)).replace(/^.*:/, "");
|
||||||
|
const getIPs = (req) => all(req, trustRemoteAddress(req));
|
||||||
|
const getSubdomains = (req, subdomainOffset = 2) => {
|
||||||
|
const hostname = getHostname(req);
|
||||||
|
if (!hostname)
|
||||||
|
return [];
|
||||||
|
const subdomains = isIP(hostname) ? [hostname] : hostname.split(".").reverse();
|
||||||
|
return subdomains.slice(subdomainOffset);
|
||||||
|
};
|
||||||
|
const onErrorHandler = function(err, _req, res) {
|
||||||
|
if (this.onError === onErrorHandler && this.parent)
|
||||||
|
return this.parent.onError(err, _req, res);
|
||||||
|
if (err instanceof Error)
|
||||||
|
console.error(err);
|
||||||
|
const code = err.code in STATUS_CODES ? err.code : err.status;
|
||||||
|
if (typeof err === "string" || Buffer.isBuffer(err))
|
||||||
|
res.writeHead(500).end(err);
|
||||||
|
else if (code in STATUS_CODES)
|
||||||
|
res.writeHead(code).end(STATUS_CODES[code]);
|
||||||
|
else
|
||||||
|
res.writeHead(500).end(err.message);
|
||||||
|
};
|
||||||
|
const renderTemplate = (_req, res, app) => (file, data, options) => {
|
||||||
|
app.render(file, data ? { ...res.locals, ...data } : res.locals, options, (err, html) => {
|
||||||
|
if (err)
|
||||||
|
throw err;
|
||||||
|
res.send(html);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
const extendMiddleware = (app) => (req, res, next) => {
|
||||||
|
const { settings } = app;
|
||||||
|
res.get = getResponseHeader(res);
|
||||||
|
req.get = getRequestHeader(req);
|
||||||
|
if (settings == null ? void 0 : settings.bindAppToReqRes) {
|
||||||
|
req.app = app;
|
||||||
|
res.app = app;
|
||||||
|
}
|
||||||
|
if (settings == null ? void 0 : settings.networkExtensions) {
|
||||||
|
req.protocol = getProtocol(req);
|
||||||
|
req.secure = req.protocol === "https";
|
||||||
|
req.hostname = getHostname(req);
|
||||||
|
req.subdomains = getSubdomains(req, settings.subdomainOffset);
|
||||||
|
req.ip = getIP(req);
|
||||||
|
req.ips = getIPs(req);
|
||||||
|
}
|
||||||
|
req.query = getQueryParams(req.url);
|
||||||
|
req.range = getRangeFromHeader(req);
|
||||||
|
req.accepts = getAccepts(req);
|
||||||
|
req.acceptsCharsets = getAcceptsCharsets(req);
|
||||||
|
req.acceptsEncodings = getAcceptsEncodings(req);
|
||||||
|
req.acceptsLanguages = getAcceptsLanguages(req);
|
||||||
|
req.xhr = checkIfXMLHttpRequest(req);
|
||||||
|
res.header = res.set = setHeader(res);
|
||||||
|
res.send = send(req, res);
|
||||||
|
res.json = json(res);
|
||||||
|
res.status = status(res);
|
||||||
|
res.sendStatus = sendStatus(req, res);
|
||||||
|
res.sendFile = sendFile(req, res);
|
||||||
|
res.type = setContentType(res);
|
||||||
|
res.location = setLocationHeader(req, res);
|
||||||
|
res.links = setLinksHeader(res);
|
||||||
|
res.vary = setVaryHeader(res);
|
||||||
|
res.cookie = setCookie(req, res);
|
||||||
|
res.clearCookie = clearCookie(req, res);
|
||||||
|
res.render = renderTemplate(req, res, app);
|
||||||
|
res.format = formatResponse(req, res, next);
|
||||||
|
res.redirect = redirect(req, res, next);
|
||||||
|
res.attachment = attachment(res);
|
||||||
|
res.download = download(req, res);
|
||||||
|
res.append = append(res);
|
||||||
|
res.locals = res.locals || /* @__PURE__ */ Object.create(null);
|
||||||
|
Object.defineProperty(req, "fresh", { get: getFreshOrStale.bind(null, req, res), configurable: true });
|
||||||
|
req.stale = !req.fresh;
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
function tryStat(path) {
|
||||||
|
try {
|
||||||
|
return statSync(path);
|
||||||
|
} catch (e) {
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class View {
|
||||||
|
constructor(name, opts = {}) {
|
||||||
|
this.ext = extname(name);
|
||||||
|
this.name = name;
|
||||||
|
this.root = opts.root;
|
||||||
|
this.defaultEngine = opts.defaultEngine;
|
||||||
|
if (!this.ext && !this.defaultEngine)
|
||||||
|
throw new Error("No default engine was specified and no extension was provided.");
|
||||||
|
let fileName = name;
|
||||||
|
if (!this.ext) {
|
||||||
|
this.ext = this.defaultEngine[0] !== "." ? "." + this.defaultEngine : this.defaultEngine;
|
||||||
|
fileName += this.ext;
|
||||||
|
}
|
||||||
|
if (!opts.engines[this.ext])
|
||||||
|
throw new Error(`No engine was found for ${this.ext}`);
|
||||||
|
this.engine = opts.engines[this.ext];
|
||||||
|
this.path = this.#lookup(fileName);
|
||||||
|
}
|
||||||
|
#lookup(name) {
|
||||||
|
let path;
|
||||||
|
const roots = [].concat(this.root);
|
||||||
|
for (let i = 0; i < roots.length && !path; i++) {
|
||||||
|
const root = roots[i];
|
||||||
|
const loc = resolve(root, name);
|
||||||
|
const dir = dirname(loc);
|
||||||
|
const file = basename(loc);
|
||||||
|
path = this.#resolve(dir, file);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#resolve(dir, file) {
|
||||||
|
const ext = this.ext;
|
||||||
|
let path = join(dir, file);
|
||||||
|
let stat = tryStat(path);
|
||||||
|
if (stat && stat.isFile()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
path = join(dir, basename(file, ext), "index" + ext);
|
||||||
|
stat = tryStat(path);
|
||||||
|
if (stat && stat.isFile()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render(options, data, cb) {
|
||||||
|
this.engine(this.path, data, options, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lead = (x) => x.charCodeAt(0) === 47 ? x : "/" + x;
|
||||||
|
const mount = (fn) => fn instanceof App ? fn.attach : fn;
|
||||||
|
const applyHandler = (h) => async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
if (h[Symbol.toStringTag] === "AsyncFunction") {
|
||||||
|
await h(req, res, next);
|
||||||
|
} else
|
||||||
|
h(req, res, next);
|
||||||
|
} catch (e) {
|
||||||
|
next(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class App extends Router {
|
||||||
|
constructor(options = {}) {
|
||||||
|
super();
|
||||||
|
this.middleware = [];
|
||||||
|
this.locals = {};
|
||||||
|
this.engines = {};
|
||||||
|
this.onError = (options == null ? void 0 : options.onError) || onErrorHandler;
|
||||||
|
this.noMatchHandler = (options == null ? void 0 : options.noMatchHandler) || this.onError.bind(this, { code: 404 });
|
||||||
|
this.settings = {
|
||||||
|
view: View,
|
||||||
|
xPoweredBy: true,
|
||||||
|
views: `${process.cwd()}/views`,
|
||||||
|
"view cache": process.env.NODE_ENV === "production",
|
||||||
|
...options.settings
|
||||||
|
};
|
||||||
|
this.applyExtensions = options == null ? void 0 : options.applyExtensions;
|
||||||
|
this.attach = (req, res) => setImmediate(this.handler.bind(this, req, res, void 0), req, res);
|
||||||
|
this.cache = {};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set app setting
|
||||||
|
* @param setting setting name
|
||||||
|
* @param value setting value
|
||||||
|
*/
|
||||||
|
set(setting, value) {
|
||||||
|
this.settings[setting] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable app setting
|
||||||
|
* @param setting Setting name
|
||||||
|
*/
|
||||||
|
enable(setting) {
|
||||||
|
this.settings[setting] = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if setting is enabled
|
||||||
|
* @param setting Setting name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
enabled(setting) {
|
||||||
|
return Boolean(this.settings[setting]);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Disable app setting
|
||||||
|
* @param setting Setting name
|
||||||
|
*/
|
||||||
|
disable(setting) {
|
||||||
|
this.settings[setting] = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return the app's absolute pathname
|
||||||
|
* based on the parent(s) that have
|
||||||
|
* mounted it.
|
||||||
|
*
|
||||||
|
* For example if the application was
|
||||||
|
* mounted as `"/admin"`, which itself
|
||||||
|
* was mounted as `"/blog"` then the
|
||||||
|
* return value would be `"/blog/admin"`.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
path() {
|
||||||
|
return this.parent ? this.parent.path() + this.mountpath : "";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Register a template engine with extension
|
||||||
|
*/
|
||||||
|
engine(ext, fn) {
|
||||||
|
this.engines[ext[0] === "." ? ext : `.${ext}`] = fn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Render a template
|
||||||
|
* @param file What to render
|
||||||
|
* @param data data that is passed to a template
|
||||||
|
* @param options Template engine options
|
||||||
|
* @param cb Callback that consumes error and html
|
||||||
|
*/
|
||||||
|
render(name, data = {}, options = {}, cb) {
|
||||||
|
let view;
|
||||||
|
const { _locals, ...opts } = options;
|
||||||
|
let locals = this.locals;
|
||||||
|
if (_locals)
|
||||||
|
locals = { ...locals, ..._locals };
|
||||||
|
locals = { ...locals, ...data };
|
||||||
|
if (opts.cache == null)
|
||||||
|
opts.cache = this.enabled("view cache");
|
||||||
|
if (opts.cache) {
|
||||||
|
view = this.cache[name];
|
||||||
|
}
|
||||||
|
if (!view) {
|
||||||
|
const View2 = this.settings["view"];
|
||||||
|
view = new View2(name, {
|
||||||
|
defaultEngine: this.settings["view engine"],
|
||||||
|
root: this.settings.views,
|
||||||
|
engines: this.engines
|
||||||
|
});
|
||||||
|
if (!view.path) {
|
||||||
|
const dirs = Array.isArray(view.root) && view.root.length > 1 ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' : 'directory "' + view.root + '"';
|
||||||
|
const err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
if (opts.cache) {
|
||||||
|
this.cache[name] = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
view.render(opts, locals, cb);
|
||||||
|
} catch (err) {
|
||||||
|
cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use(...args) {
|
||||||
|
const base = args[0];
|
||||||
|
const fns = args.slice(1).flat();
|
||||||
|
let pathArray = [];
|
||||||
|
if (typeof base === "function" || base instanceof App) {
|
||||||
|
fns.unshift(base);
|
||||||
|
} else {
|
||||||
|
let basePaths = [];
|
||||||
|
if (Array.isArray(base))
|
||||||
|
basePaths = [...base];
|
||||||
|
else if (typeof base === "string")
|
||||||
|
basePaths = [base];
|
||||||
|
basePaths = basePaths.filter((element) => {
|
||||||
|
if (typeof element === "string") {
|
||||||
|
pathArray.push(element);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
fns.unshift(...basePaths);
|
||||||
|
}
|
||||||
|
pathArray = pathArray.length ? pathArray.map((path) => lead(path)) : ["/"];
|
||||||
|
const mountpath = pathArray.join(", ");
|
||||||
|
let regex;
|
||||||
|
for (const fn of fns) {
|
||||||
|
if (fn instanceof App) {
|
||||||
|
pathArray.forEach((path) => {
|
||||||
|
regex = parse(path, true);
|
||||||
|
fn.mountpath = mountpath;
|
||||||
|
this.apps[path] = fn;
|
||||||
|
fn.parent = this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pathArray.forEach((path) => {
|
||||||
|
var _a;
|
||||||
|
const handlerPaths = [];
|
||||||
|
const handlerFunctions = [];
|
||||||
|
const handlerPathBase = path === "/" ? "" : lead(path);
|
||||||
|
for (const fn of fns) {
|
||||||
|
if (fn instanceof App && ((_a = fn.middleware) == null ? void 0 : _a.length)) {
|
||||||
|
for (const mw of fn.middleware) {
|
||||||
|
handlerPaths.push(handlerPathBase + lead(mw.path));
|
||||||
|
handlerFunctions.push(fn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handlerPaths.push("");
|
||||||
|
handlerFunctions.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushMiddleware(this.middleware)({
|
||||||
|
path,
|
||||||
|
regex,
|
||||||
|
type: "mw",
|
||||||
|
handler: mount(handlerFunctions[0]),
|
||||||
|
handlers: handlerFunctions.slice(1).map(mount),
|
||||||
|
fullPaths: handlerPaths
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
route(path) {
|
||||||
|
const app = new App({ settings: this.settings });
|
||||||
|
this.use(path, app);
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
#find(url) {
|
||||||
|
return this.middleware.filter((m) => {
|
||||||
|
m.regex = m.regex || parse(m.path, m.type === "mw");
|
||||||
|
let fullPathRegex;
|
||||||
|
m.fullPath && typeof m.fullPath === "string" ? fullPathRegex = parse(m.fullPath, m.type === "mw") : fullPathRegex = null;
|
||||||
|
return m.regex.pattern.test(url) && (m.type === "mw" && fullPathRegex ? fullPathRegex.pattern.test(url) : true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extends Req / Res objects, pushes 404 and 500 handlers, dispatches middleware
|
||||||
|
* @param req Req object
|
||||||
|
* @param res Res object
|
||||||
|
*/
|
||||||
|
handler(req, res, next) {
|
||||||
|
const { xPoweredBy } = this.settings;
|
||||||
|
if (xPoweredBy)
|
||||||
|
res.setHeader("X-Powered-By", typeof xPoweredBy === "string" ? xPoweredBy : "tinyhttp");
|
||||||
|
const exts = this.applyExtensions || extendMiddleware(this);
|
||||||
|
req.originalUrl = req.url || req.originalUrl;
|
||||||
|
const pathname = getPathname(req.originalUrl);
|
||||||
|
const matched = this.#find(pathname);
|
||||||
|
const mw = [
|
||||||
|
{
|
||||||
|
handler: exts,
|
||||||
|
type: "mw",
|
||||||
|
path: "/"
|
||||||
|
},
|
||||||
|
...matched.filter((x) => req.method === "HEAD" || (x.method ? x.method === req.method : true))
|
||||||
|
];
|
||||||
|
if (matched[0] != null) {
|
||||||
|
mw.push({
|
||||||
|
type: "mw",
|
||||||
|
handler: (req2, res2, next2) => {
|
||||||
|
if (req2.method === "HEAD") {
|
||||||
|
res2.statusCode = 204;
|
||||||
|
return res2.end("");
|
||||||
|
}
|
||||||
|
next2();
|
||||||
|
},
|
||||||
|
path: "/"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mw.push({
|
||||||
|
handler: this.noMatchHandler,
|
||||||
|
type: "mw",
|
||||||
|
path: "/"
|
||||||
|
});
|
||||||
|
const handle = (mw2) => async (req2, res2, next2) => {
|
||||||
|
var _a;
|
||||||
|
const { path, handler, regex } = mw2;
|
||||||
|
let params;
|
||||||
|
try {
|
||||||
|
params = regex ? getURLParams(regex, pathname) : {};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e instanceof URIError)
|
||||||
|
return res2.sendStatus(400);
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
let prefix = path;
|
||||||
|
if (regex) {
|
||||||
|
for (const key of regex.keys) {
|
||||||
|
if (key === "wild") {
|
||||||
|
prefix = prefix.replace("*", params.wild);
|
||||||
|
} else {
|
||||||
|
prefix = prefix.replace(`:${key}`, params[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req2.params = { ...req2.params, ...params };
|
||||||
|
if (mw2.type === "mw") {
|
||||||
|
req2.url = lead(req2.originalUrl.substring(prefix.length));
|
||||||
|
}
|
||||||
|
if (!req2.path)
|
||||||
|
req2.path = getPathname(req2.url);
|
||||||
|
if ((_a = this.settings) == null ? void 0 : _a.enableReqRoute)
|
||||||
|
req2.route = mw2;
|
||||||
|
await applyHandler(handler)(req2, res2, next2);
|
||||||
|
};
|
||||||
|
let idx = 0;
|
||||||
|
const loop = () => res.writableEnded || idx < mw.length && handle(mw[idx++])(req, res, next);
|
||||||
|
next = next || ((err) => err ? this.onError(err, req, res) : loop());
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates HTTP server and dispatches middleware
|
||||||
|
* @param port server listening port
|
||||||
|
* @param Server callback after server starts listening
|
||||||
|
* @param host server listening host
|
||||||
|
*/
|
||||||
|
listen(port, cb, host) {
|
||||||
|
return createServer().on("request", this.attach).listen(port, host, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
App,
|
||||||
|
View,
|
||||||
|
extendMiddleware,
|
||||||
|
getHostname,
|
||||||
|
getIP,
|
||||||
|
getIPs,
|
||||||
|
getProtocol,
|
||||||
|
getSubdomains,
|
||||||
|
getURLParams2 as getURLParams,
|
||||||
|
onErrorHandler,
|
||||||
|
renderTemplate
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,7 @@
|
||||||
|
import type { NextFunction } from '@tinyhttp/router';
|
||||||
|
import type { Request } from './request.js';
|
||||||
|
import type { Response } from './response.js';
|
||||||
|
import type { App } from './app.js';
|
||||||
|
export type ErrorHandler = (this: App, err: any, req: Request, res: Response, next?: NextFunction) => void;
|
||||||
|
export declare const onErrorHandler: ErrorHandler;
|
||||||
|
//# sourceMappingURL=onError.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"onError.d.ts","sourceRoot":"","sources":["../src/onError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;AAE1G,eAAO,MAAM,cAAc,EAAE,YAU5B,CAAA"}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { IncomingMessage } from 'node:http';
|
||||||
|
import type { ParsedUrlQuery } from 'node:querystring';
|
||||||
|
import { Options, Ranges } from 'header-range-parser';
|
||||||
|
import { App } from './app.js';
|
||||||
|
import type { Middleware } from '@tinyhttp/router';
|
||||||
|
import type { URLParams } from '@tinyhttp/req';
|
||||||
|
import type { Socket } from 'node:net';
|
||||||
|
import type { TLSSocket } from 'node:tls';
|
||||||
|
export { getURLParams } from '@tinyhttp/req';
|
||||||
|
export declare const getProtocol: (req: Request) => Protocol;
|
||||||
|
export declare const getHostname: (req: Request) => string | undefined;
|
||||||
|
export declare const getIP: (req: Pick<Request, 'headers' | 'connection' | 'socket'>) => string | undefined;
|
||||||
|
export declare const getIPs: (req: Pick<Request, 'headers' | 'connection' | 'socket'>) => string[] | undefined;
|
||||||
|
export declare const getSubdomains: (req: Request, subdomainOffset?: number) => string[];
|
||||||
|
export type Connection = IncomingMessage['socket'] & {
|
||||||
|
encrypted: boolean;
|
||||||
|
};
|
||||||
|
export type Protocol = 'http' | 'https' | string;
|
||||||
|
export type { URLParams };
|
||||||
|
type AcceptsReturns = string | boolean | string[];
|
||||||
|
export interface Request extends IncomingMessage {
|
||||||
|
originalUrl: string;
|
||||||
|
path: string;
|
||||||
|
url: string;
|
||||||
|
query: ParsedUrlQuery;
|
||||||
|
params: URLParams;
|
||||||
|
connection: Connection;
|
||||||
|
socket: TLSSocket | Socket;
|
||||||
|
route?: Middleware;
|
||||||
|
protocol: Protocol;
|
||||||
|
secure: boolean;
|
||||||
|
xhr: boolean;
|
||||||
|
hostname?: string;
|
||||||
|
ip?: string;
|
||||||
|
ips?: string[];
|
||||||
|
subdomains?: string[];
|
||||||
|
get: (header: string) => string | string[] | undefined;
|
||||||
|
range: (size: number, options?: Options) => -1 | -2 | -3 | Ranges | undefined;
|
||||||
|
accepts: (...types: string[]) => AcceptsReturns;
|
||||||
|
acceptsEncodings: (...encodings: string[]) => AcceptsReturns;
|
||||||
|
acceptsCharsets: (...charsets: string[]) => AcceptsReturns;
|
||||||
|
acceptsLanguages: (...languages: string[]) => AcceptsReturns;
|
||||||
|
is: (...types: string[]) => boolean;
|
||||||
|
cookies?: any;
|
||||||
|
signedCookies?: any;
|
||||||
|
secret?: string | string[];
|
||||||
|
fresh?: boolean;
|
||||||
|
stale?: boolean;
|
||||||
|
body?: any;
|
||||||
|
app?: App;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=request.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":";;;;AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGrD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAQ5C,eAAO,MAAM,WAAW,QAAS,OAAO,KAAG,QAU1C,CAAA;AAED,eAAO,MAAM,WAAW,QAAS,OAAO,KAAG,MAAM,GAAG,SAWnD,CAAA;AAED,eAAO,MAAM,KAAK,QAAS,KAAK,OAAO,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC,KAAG,MAAM,GAAG,SAC5B,CAAA;AAE7D,eAAO,MAAM,MAAM,QAAS,KAAK,OAAO,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC,KAAG,MAAM,EAAE,GAAG,SACzD,CAAA;AAEnC,eAAO,MAAM,aAAa,QAAS,OAAO,+BAAwB,MAAM,EAQvE,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,GAAG;IACnD,SAAS,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AAEhD,YAAY,EAAE,SAAS,EAAE,CAAA;AAEzB,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAA;AAEjD,MAAM,WAAW,OAAQ,SAAQ,eAAe;IAC9C,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,EAAE,SAAS,CAAA;IACjB,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;IACf,GAAG,EAAE,OAAO,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;IACtD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,GAAG,SAAS,CAAA;IAC7E,OAAO,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,cAAc,CAAA;IAC/C,gBAAgB,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,KAAK,cAAc,CAAA;IAC5D,eAAe,EAAE,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,KAAK,cAAc,CAAA;IAC1D,gBAAgB,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,KAAK,cAAc,CAAA;IAC5D,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAA;IACnC,OAAO,CAAC,EAAE,GAAG,CAAA;IACb,aAAa,CAAC,EAAE,GAAG,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,GAAG,CAAA;IACV,GAAG,CAAC,EAAE,GAAG,CAAA;CACV"}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { ServerResponse } from 'node:http';
|
||||||
|
import type { SerializeOptions } from '@tinyhttp/cookie';
|
||||||
|
import { Request } from './request.js';
|
||||||
|
import { App } from './app.js';
|
||||||
|
import type { ReadStreamOptions, FormatProps, DownloadOptions } from '@tinyhttp/res';
|
||||||
|
import type { AppRenderOptions, TemplateEngineOptions } from './types.js';
|
||||||
|
export declare const renderTemplate: <O extends TemplateEngineOptions = TemplateEngineOptions>(_req: Request, res: Response, app: App) => (file: string, data?: Record<string, unknown>, options?: AppRenderOptions<O>) => Response;
|
||||||
|
export interface Response<B = unknown> extends ServerResponse {
|
||||||
|
header(field: string | Record<string, unknown>, val?: string | any[]): Response<B>;
|
||||||
|
set(field: string | Record<string, unknown>, val?: string | any[]): Response<B>;
|
||||||
|
get(field: string): string | number | string[];
|
||||||
|
send(body: B): Response<B>;
|
||||||
|
sendFile(path: string, options?: ReadStreamOptions, cb?: (err?: unknown) => void): Response<B>;
|
||||||
|
json(body: B): Response<B>;
|
||||||
|
status(status: number): Response<B>;
|
||||||
|
sendStatus(statusCode: number): Response<B>;
|
||||||
|
cookie(name: string, value: string | Record<string, unknown>, options?: SerializeOptions & Partial<{
|
||||||
|
signed: boolean;
|
||||||
|
}>): Response<B>;
|
||||||
|
clearCookie(name: string, options?: SerializeOptions): Response<B>;
|
||||||
|
location(url: string): Response<B>;
|
||||||
|
links(links: {
|
||||||
|
[key: string]: string;
|
||||||
|
}): Response<B>;
|
||||||
|
render<O extends TemplateEngineOptions = TemplateEngineOptions>(file: string, data?: Record<string, any>, options?: AppRenderOptions<O>): Response<B>;
|
||||||
|
vary(field: string): Response<B>;
|
||||||
|
format(obj: FormatProps): Response<B>;
|
||||||
|
redirect(url: string, status?: number): Response<B>;
|
||||||
|
type(type: string): Response<B>;
|
||||||
|
download(path: string, filename: string, options?: DownloadOptions, cb?: (err?: unknown) => void): Response<B>;
|
||||||
|
attachment(filename?: string): Response<B>;
|
||||||
|
app?: App;
|
||||||
|
locals: Record<string, any>;
|
||||||
|
/**
|
||||||
|
* Send JSON response with JSONP callback support.
|
||||||
|
*
|
||||||
|
* To enable this method, install the `@tinyhttp/jsonp` package and attach the method to `res.jsonp` property.
|
||||||
|
*
|
||||||
|
* @param obj Response object
|
||||||
|
*/
|
||||||
|
jsonp(obj: any): Response<B>;
|
||||||
|
append(field: string, value: any): Response<B>;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=response.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEzE,eAAO,MAAM,cAAc,kEACuC,OAAO,OAAO,QAAQ,OAAO,GAAG,YACzF,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,oCAAkC,QAM9E,CAAA;AAEH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,cAAc;IAC3D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAClF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC/E,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;IAC9C,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC9F,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACnC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC3C,MAAM,CACJ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,GACxD,QAAQ,CAAC,CAAC,CAAC,CAAA;IACd,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAClE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAClC,KAAK,CAAC,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACpD,MAAM,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,EAC5D,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,QAAQ,CAAC,CAAC,CAAC,CAAA;IACd,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAChC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACnD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC9G,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC1C,GAAG,CAAC,EAAE,GAAG,CAAA;IACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IAE5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;CAC/C"}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import type { Handler, NextFunction } from '@tinyhttp/router';
|
||||||
|
import type { ErrorHandler } from './onError.js';
|
||||||
|
import type { Request } from './request.js';
|
||||||
|
import type { Response } from './response.js';
|
||||||
|
import type { View } from './view.js';
|
||||||
|
/**
|
||||||
|
* tinyhttp App has a few settings for toggling features
|
||||||
|
*/
|
||||||
|
export type AppSettings = Partial<{
|
||||||
|
networkExtensions: boolean;
|
||||||
|
subdomainOffset: number;
|
||||||
|
bindAppToReqRes: boolean;
|
||||||
|
xPoweredBy: string | boolean;
|
||||||
|
enableReqRoute: boolean;
|
||||||
|
views: string | string[];
|
||||||
|
view: typeof View;
|
||||||
|
'view cache': boolean;
|
||||||
|
'view engine': string;
|
||||||
|
}>;
|
||||||
|
export type TemplateEngineOptions = {
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Function that processes the template
|
||||||
|
*/
|
||||||
|
export type TemplateEngine<O extends TemplateEngineOptions = TemplateEngineOptions> = (path: string, locals: Record<string, unknown>, opts: AppRenderOptions<O>, cb: (err: Error | null, html: unknown) => void) => void;
|
||||||
|
export type AppRenderOptions<O extends TemplateEngineOptions = TemplateEngineOptions> = O & Partial<{
|
||||||
|
cache: boolean;
|
||||||
|
ext: string;
|
||||||
|
viewsFolder: string;
|
||||||
|
_locals: Record<string, unknown>;
|
||||||
|
}>;
|
||||||
|
export type AppConstructor<Req extends Request = Request, Res extends Response = Response> = Partial<{
|
||||||
|
noMatchHandler: Handler<Req, Res>;
|
||||||
|
onError: ErrorHandler;
|
||||||
|
settings: AppSettings;
|
||||||
|
applyExtensions: (req: Request, res: Response, next: NextFunction) => void;
|
||||||
|
}>;
|
||||||
|
//# sourceMappingURL=types.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,OAAO,CAAA;IACxB,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5B,cAAc,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACxB,IAAI,EAAE,OAAO,IAAI,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,IAAI,CACpF,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EACzB,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,KAC3C,IAAI,CAAA;AAET,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB,IAAI,CAAC,GACvF,OAAO,CAAC;IACN,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAC,CAAA;AAEJ,MAAM,MAAM,cAAc,CAAC,GAAG,SAAS,OAAO,GAAG,OAAO,EAAE,GAAG,SAAS,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC;IACnG,cAAc,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACjC,OAAO,EAAE,YAAY,CAAA;IACrB,QAAQ,EAAE,WAAW,CAAA;IACrB,eAAe,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;CAC3E,CAAC,CAAA"}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*!
|
||||||
|
* Ported from https://github.com/expressjs/express/blob/master/lib/view.js
|
||||||
|
* express
|
||||||
|
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||||
|
* Copyright(c) 2013 Roman Shtylman
|
||||||
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
import { TemplateEngineOptions, TemplateEngine } from './types.js';
|
||||||
|
/**
|
||||||
|
* Initialize a new `View` with the given `name`.
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
*
|
||||||
|
* - `defaultEngine` the default template engine name
|
||||||
|
* - `engines` template engine require() cache
|
||||||
|
* - `root` root path for view lookup
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param options
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export declare class View<RenderOptions extends TemplateEngineOptions = TemplateEngineOptions> {
|
||||||
|
#private;
|
||||||
|
ext: string;
|
||||||
|
defaultEngine: string;
|
||||||
|
name: string;
|
||||||
|
engine: TemplateEngine<RenderOptions>;
|
||||||
|
path: string;
|
||||||
|
root: string | string[];
|
||||||
|
constructor(name: string, opts?: Partial<{
|
||||||
|
defaultEngine: string;
|
||||||
|
root: string | string[];
|
||||||
|
engines: Record<string, TemplateEngine<RenderOptions>>;
|
||||||
|
}>);
|
||||||
|
render(options: RenderOptions, data: Record<string, unknown>, cb: (err: Error | null, html: unknown) => void): void;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=view.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAWlE;;;;;;;;;;;;GAYG;AAEH,qBAAa,IAAI,CAAC,aAAa,SAAS,qBAAqB,GAAG,qBAAqB;;IACnF,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAA;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;gBAErB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,OAAO,CAAC;QACZ,aAAa,EAAE,MAAM,CAAA;QACrB,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAA;KACvD,CAAM;IA4DT,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI;CAG7G"}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/app",
|
||||||
|
"version": "2.2.3",
|
||||||
|
"description": "0-legacy, tiny & fast web framework as a replacement of Express",
|
||||||
|
"type": "module",
|
||||||
|
"homepage": "https://tinyhttp.v1rtl.site",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/app"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
|
||||||
|
},
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.21.3"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tinyhttp",
|
||||||
|
"router",
|
||||||
|
"backend",
|
||||||
|
"http",
|
||||||
|
"framework",
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"author": "v1rtl",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"header-range-parser": "1.1.3",
|
||||||
|
"regexparam": "^2.0.1",
|
||||||
|
"@tinyhttp/cookie": "2.1.0",
|
||||||
|
"@tinyhttp/res": "2.2.2",
|
||||||
|
"@tinyhttp/proxy-addr": "2.1.3",
|
||||||
|
"@tinyhttp/req": "2.2.2",
|
||||||
|
"@tinyhttp/router": "2.2.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
107
comments/Home/node_modules/@tinyhttp/content-disposition/README.md
generated
vendored
Normal file
107
comments/Home/node_modules/@tinyhttp/content-disposition/README.md
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# @tinyhttp/content-disposition
|
||||||
|
|
||||||
|
> [`content-disposition`](https://github.com/jshttp/content-disposition) rewrite
|
||||||
|
> in TypeScript.
|
||||||
|
|
||||||
|
Create and parse HTTP `Content-Disposition` header
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/content-disposition
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { contentDisposition, parse } from '@tinyhttp/content-disposition'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `contentDisposition(filename)`
|
||||||
|
|
||||||
|
Create an attachment `Content-Disposition` header value using the given file
|
||||||
|
name, if supplied. The `filename` is optional and if no file name is desired,
|
||||||
|
but you want to specify `options`, set `filename` to `undefined`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
res.setHeader('Content-Disposition', contentDisposition('∫ maths.pdf'))
|
||||||
|
```
|
||||||
|
|
||||||
|
**note** HTTP headers are of the ISO-8859-1 character set. If you are writing
|
||||||
|
this header through a means different from `setHeader` in Node.js, you'll want
|
||||||
|
to specify the `'binary'` encoding in Node.js.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
`contentDisposition` accepts these properties in the options object.
|
||||||
|
|
||||||
|
##### `fallback`
|
||||||
|
|
||||||
|
If the `filename` option is outside ISO-8859-1, then the file name is actually
|
||||||
|
stored in a supplemental field for clients that support Unicode file names and a
|
||||||
|
ISO-8859-1 version of the file name is automatically generated.
|
||||||
|
|
||||||
|
This specifies the ISO-8859-1 file name to override the automatic generation or
|
||||||
|
disables the generation all together, defaults to `true`.
|
||||||
|
|
||||||
|
- A string will specify the ISO-8859-1 file name to use in place of automatic
|
||||||
|
generation.
|
||||||
|
- `false` will disable including a ISO-8859-1 file name and only include the
|
||||||
|
Unicode version (unless the file name is already ISO-8859-1).
|
||||||
|
- `true` will enable automatic generation if the file name is outside
|
||||||
|
ISO-8859-1.
|
||||||
|
|
||||||
|
If the `filename` option is ISO-8859-1 and this option is specified and has a
|
||||||
|
different value, then the `filename` option is encoded in the extended field and
|
||||||
|
this set as the fallback field, even though they are both ISO-8859-1.
|
||||||
|
|
||||||
|
##### `type`
|
||||||
|
|
||||||
|
Specifies the disposition type, defaults to `"attachment"`. This can also be
|
||||||
|
`"inline"`, or any other value (all values except inline are treated like
|
||||||
|
`attachment`, but can convey additional information if both parties agree to
|
||||||
|
it). The type is normalized to lower-case.
|
||||||
|
|
||||||
|
### `contentDisposition.parse(string)`
|
||||||
|
|
||||||
|
```js
|
||||||
|
contentDisposition.parse('attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt')
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse a `Content-Disposition` header string. This automatically handles extended
|
||||||
|
("Unicode") parameters by decoding them and providing them under the standard
|
||||||
|
parameter name. This will return an object with the following properties
|
||||||
|
(examples are shown for the string
|
||||||
|
`'attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt'`):
|
||||||
|
|
||||||
|
- `type`: The disposition type (always lower case). Example: `'attachment'`
|
||||||
|
|
||||||
|
- `parameters`: An object of the parameters in the disposition (name of
|
||||||
|
parameter always lower case and extended versions replace non-extended
|
||||||
|
versions). Example: `{filename: "€ rates.txt"}`
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This simple example shows how to use `accepts` to return a different typed
|
||||||
|
respond body based on what the client wants to accept. The server lists it's
|
||||||
|
preferences in order and will get back the best match between the client and
|
||||||
|
server.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { contentDisposition } from '@tinyhttp/content-disposition'
|
||||||
|
import destroy from 'destroy'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import { createServer } from 'node:http'
|
||||||
|
import onFinished from 'on-finished'
|
||||||
|
|
||||||
|
const filePath = '/path/to/public/plans.pdf'
|
||||||
|
|
||||||
|
createServer((req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'application/pdf')
|
||||||
|
res.setHeader('Content-Disposition', contentDisposition(filePath))
|
||||||
|
|
||||||
|
const stream = fs.createReadStream(filePath)
|
||||||
|
stream.pipe(res)
|
||||||
|
onFinished(res, () => destroy(stream))
|
||||||
|
})
|
||||||
|
```
|
20
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.d.ts
generated
vendored
Normal file
20
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
export declare class ContentDisposition {
|
||||||
|
type: string;
|
||||||
|
parameters: Record<string, unknown>;
|
||||||
|
constructor(type: string, parameters: Record<string, unknown>);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create an attachment Content-Disposition header.
|
||||||
|
*
|
||||||
|
* @param filename file name
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export declare function contentDisposition(filename?: string, options?: Partial<{
|
||||||
|
type: string;
|
||||||
|
fallback: string | boolean;
|
||||||
|
}>): string;
|
||||||
|
/**
|
||||||
|
* Parse Content-Disposition header string.
|
||||||
|
* @param header string
|
||||||
|
*/
|
||||||
|
export declare function parse(header: string): ContentDisposition;
|
138
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.js
generated
vendored
Normal file
138
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.js
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
const ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g;
|
||||||
|
const HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/;
|
||||||
|
const HEX_ESCAPE_REPLACE_REGEXP = /%([0-9A-Fa-f]{2})/g;
|
||||||
|
const NON_LATIN1_REGEXP = /[^\x20-\x7e\xa0-\xff]/g;
|
||||||
|
const QESC_REGEXP = /\\([\u0000-\u007f])/g;
|
||||||
|
const QUOTE_REGEXP = /([\\"])/g;
|
||||||
|
const PARAM_REGEXP = /;[\x09\x20]*([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*=[\x09\x20]*("(?:[\x20!\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*/g;
|
||||||
|
const TEXT_REGEXP = /^[\x20-\x7e\x80-\xff]+$/;
|
||||||
|
const TOKEN_REGEXP = /^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/;
|
||||||
|
const EXT_VALUE_REGEXP = /^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+.^_`|~-])+)$/;
|
||||||
|
const DISPOSITION_TYPE_REGEXP = /^([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*(?:$|;)/;
|
||||||
|
const getlatin1 = (val) => {
|
||||||
|
return String(val).replace(NON_LATIN1_REGEXP, "?");
|
||||||
|
};
|
||||||
|
class ContentDisposition {
|
||||||
|
constructor(type, parameters) {
|
||||||
|
this.type = type;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const qstring = (val) => '"' + String(val).replace(QUOTE_REGEXP, "\\$1") + '"';
|
||||||
|
const pencode = (char) => "%" + String(char).charCodeAt(0).toString(16).toUpperCase();
|
||||||
|
function ustring(val) {
|
||||||
|
const str = String(val);
|
||||||
|
const encoded = encodeURIComponent(str).replace(ENCODE_URL_ATTR_CHAR_REGEXP, pencode);
|
||||||
|
return "UTF-8''" + encoded;
|
||||||
|
}
|
||||||
|
const basename = (str) => str.slice(str.lastIndexOf("/") + 1);
|
||||||
|
function format({
|
||||||
|
parameters,
|
||||||
|
type
|
||||||
|
}) {
|
||||||
|
if (!type || typeof type !== "string" || !TOKEN_REGEXP.test(type)) {
|
||||||
|
throw new TypeError("invalid type");
|
||||||
|
}
|
||||||
|
let string = String(type).toLowerCase();
|
||||||
|
if (parameters && typeof parameters === "object") {
|
||||||
|
const params = Object.keys(parameters).sort();
|
||||||
|
for (const param of params) {
|
||||||
|
const val = param.slice(-1) === "*" ? ustring(parameters[param]) : qstring(parameters[param]);
|
||||||
|
string += "; " + param + "=" + val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
function createParams(filename, fallback) {
|
||||||
|
if (filename === void 0)
|
||||||
|
return;
|
||||||
|
const params = {};
|
||||||
|
if (fallback === void 0)
|
||||||
|
fallback = true;
|
||||||
|
if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
|
||||||
|
throw new TypeError("fallback must be ISO-8859-1 string");
|
||||||
|
}
|
||||||
|
const name = basename(filename);
|
||||||
|
const isQuotedString = TEXT_REGEXP.test(name);
|
||||||
|
const fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename(fallback);
|
||||||
|
const hasFallback = typeof fallbackName === "string" && fallbackName !== name;
|
||||||
|
if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
|
||||||
|
params["filename*"] = name;
|
||||||
|
}
|
||||||
|
if (isQuotedString || hasFallback) {
|
||||||
|
params.filename = hasFallback ? fallbackName : name;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
const pdecode = (_str, hex) => String.fromCharCode(parseInt(hex, 16));
|
||||||
|
function contentDisposition(filename, options = {}) {
|
||||||
|
return format(new ContentDisposition(options.type || "attachment", createParams(filename, options.fallback)));
|
||||||
|
}
|
||||||
|
function decodefield(str) {
|
||||||
|
const match = EXT_VALUE_REGEXP.exec(str);
|
||||||
|
if (!match)
|
||||||
|
throw new TypeError("invalid extended field value");
|
||||||
|
const charset = match[1].toLowerCase();
|
||||||
|
const encoded = match[2];
|
||||||
|
let value;
|
||||||
|
switch (charset) {
|
||||||
|
case "iso-8859-1":
|
||||||
|
value = getlatin1(encoded.replace(HEX_ESCAPE_REPLACE_REGEXP, pdecode));
|
||||||
|
break;
|
||||||
|
case "utf-8":
|
||||||
|
try {
|
||||||
|
value = decodeURIComponent(encoded);
|
||||||
|
} catch {
|
||||||
|
throw new TypeError("invalid encoded utf-8");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new TypeError("unsupported charset in extended field");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function parse(header) {
|
||||||
|
let match = DISPOSITION_TYPE_REGEXP.exec(header);
|
||||||
|
if (!match)
|
||||||
|
throw new TypeError("invalid type format");
|
||||||
|
let index = match[0].length;
|
||||||
|
const type = match[1].toLowerCase();
|
||||||
|
let key;
|
||||||
|
const names = [];
|
||||||
|
const params = {};
|
||||||
|
let value;
|
||||||
|
index = PARAM_REGEXP.lastIndex = match[0].slice(-1) === ";" ? index - 1 : index;
|
||||||
|
while (match = PARAM_REGEXP.exec(header)) {
|
||||||
|
if (match.index !== index)
|
||||||
|
throw new TypeError("invalid parameter format");
|
||||||
|
index += match[0].length;
|
||||||
|
key = match[1].toLowerCase();
|
||||||
|
value = match[2];
|
||||||
|
if (names.indexOf(key) !== -1) {
|
||||||
|
throw new TypeError("invalid duplicate parameter");
|
||||||
|
}
|
||||||
|
names.push(key);
|
||||||
|
if (key.indexOf("*") + 1 === key.length) {
|
||||||
|
key = key.slice(0, -1);
|
||||||
|
value = decodefield(value);
|
||||||
|
params[key] = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (typeof params[key] === "string")
|
||||||
|
continue;
|
||||||
|
if (value[0] === '"') {
|
||||||
|
value = value.slice(1, value.length - 1).replace(QESC_REGEXP, "$1");
|
||||||
|
}
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
if (index !== -1 && index !== header.length) {
|
||||||
|
throw new TypeError("invalid parameter format");
|
||||||
|
}
|
||||||
|
return new ContentDisposition(type, params);
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
ContentDisposition,
|
||||||
|
contentDisposition,
|
||||||
|
parse
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
1
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.js.map
generated
vendored
Normal file
1
comments/Home/node_modules/@tinyhttp/content-disposition/dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
30
comments/Home/node_modules/@tinyhttp/content-disposition/package.json
generated
vendored
Normal file
30
comments/Home/node_modules/@tinyhttp/content-disposition/package.json
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/content-disposition",
|
||||||
|
"description": "content-disposition rewrite in TypeScript",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://tinyhttp.v1rtl.site",
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/content-disposition"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"dependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,82 @@
|
||||||
|
# @tinyhttp/content-type
|
||||||
|
|
||||||
|
[![Version][v-badge-url]][npm-url] [![Downloads][dl-badge-url]][npm-url] [![GitHub Workflow Status][gh-actions-img]][github-actions] [![Codecov][cov-badge-url]][cov-url]
|
||||||
|
|
||||||
|
> [`content-type`](https://github.com/jshttp/content-type) rewrite in TypeScript and ESM.
|
||||||
|
|
||||||
|
Create and parse HTTP Content-Type header according to RFC 7231
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/content-type
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { parse, format } from '@tinyhttp/content-type'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `parse(string: string | Request | Response)`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const obj = parse('image/svg+xml; charset=utf-8')
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse a `Content-Type` header. This will return an object with the following
|
||||||
|
properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type (the type and subtype, always lower case).
|
||||||
|
Example: `'image/svg+xml'`
|
||||||
|
|
||||||
|
- `parameters`: An object of the parameters in the media type (name of parameter
|
||||||
|
always lower case). Example: `{charset: 'utf-8'}`
|
||||||
|
|
||||||
|
Throws a `TypeError` if the string is missing or invalid.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const obj = contentType.parse(req)
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse the `Content-Type` header from the given `req`. Short-cut for
|
||||||
|
`contentType.parse(req.headers['content-type'])`.
|
||||||
|
|
||||||
|
Throws a `TypeError` if the `Content-Type` header is missing or invalid.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const obj = contentType.parse(res)
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse the `Content-Type` header set on the given `res`. Short-cut for
|
||||||
|
`contentType.parse(res.getHeader('content-type'))`.
|
||||||
|
|
||||||
|
Throws a `TypeError` if the `Content-Type` header is missing or invalid.
|
||||||
|
|
||||||
|
### `format(obj)`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const str = contentType.format({
|
||||||
|
type: 'image/svg+xml',
|
||||||
|
parameters: { charset: 'utf-8' },
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Format an object into a `Content-Type` header. This will return a string of the
|
||||||
|
content type for the given object with the following properties (examples are
|
||||||
|
shown that produce the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type (will be lower-cased). Example: `'image/svg+xml'`
|
||||||
|
|
||||||
|
- `parameters`: An object of the parameters in the media type (name of the
|
||||||
|
parameter will be lower-cased). Example: `{charset: 'utf-8'}`
|
||||||
|
|
||||||
|
Throws a `TypeError` if the object contains an invalid type or parameter names.
|
||||||
|
|
||||||
|
[v-badge-url]: https://img.shields.io/npm/v/@tinyhttp/content-type.svg?style=for-the-badge&color=FF69B4&label=&logo=npm
|
||||||
|
[npm-url]: https://www.npmjs.com/package/@tinyhttp/content-type
|
||||||
|
[cov-badge-url]: https://img.shields.io/coveralls/github/tinyhttp/content-type?style=for-the-badge&color=FF69B4
|
||||||
|
[cov-url]: https://coveralls.io/github/tinyhttp/@tinyhttp/content-type
|
||||||
|
[dl-badge-url]: https://img.shields.io/npm/dt/@tinyhttp/content-type?style=for-the-badge&color=FF69B4
|
||||||
|
[github-actions]: https://github.com/tinyhttp/content-type/actions
|
||||||
|
[gh-actions-img]: https://img.shields.io/github/actions/workflow/status/tinyhttp/content-type/ci.yml?branch=master&style=for-the-badge&color=FF69B4&label=&logo=github
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { IncomingHttpHeaders, ServerResponse } from 'node:http';
|
||||||
|
|
||||||
|
type Request = {
|
||||||
|
headers: IncomingHttpHeaders;
|
||||||
|
};
|
||||||
|
type Response = Pick<ServerResponse, 'getHeader'>;
|
||||||
|
/**
|
||||||
|
* Class to represent a content type.
|
||||||
|
*/
|
||||||
|
declare class ContentType {
|
||||||
|
parameters?: Record<string, unknown>;
|
||||||
|
type: string;
|
||||||
|
constructor(type: string);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Format object to media type.
|
||||||
|
*/
|
||||||
|
declare function format(obj: ContentType): string;
|
||||||
|
/**
|
||||||
|
* Parse media type to object.
|
||||||
|
*/
|
||||||
|
declare function parse(string: string | Request | Response): ContentType;
|
||||||
|
|
||||||
|
export { format, parse };
|
|
@ -0,0 +1,89 @@
|
||||||
|
// src/index.ts
|
||||||
|
var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;
|
||||||
|
var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;
|
||||||
|
var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g;
|
||||||
|
var QUOTE_REGEXP = /([\\"])/g;
|
||||||
|
var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
function qstring(val) {
|
||||||
|
const str = String(val);
|
||||||
|
if (TOKEN_REGEXP.test(str))
|
||||||
|
return str;
|
||||||
|
if (str.length > 0 && !TEXT_REGEXP.test(str))
|
||||||
|
throw new TypeError("invalid parameter value");
|
||||||
|
return '"' + str.replace(QUOTE_REGEXP, "\\$1") + '"';
|
||||||
|
}
|
||||||
|
function getcontenttype(obj) {
|
||||||
|
let header;
|
||||||
|
if ("getHeader" in obj && typeof obj.getHeader === "function") {
|
||||||
|
header = obj.getHeader("content-type");
|
||||||
|
} else if ("headers" in obj && typeof obj.headers === "object") {
|
||||||
|
const h = obj.headers;
|
||||||
|
header = h && h["content-type"];
|
||||||
|
}
|
||||||
|
if (typeof header !== "string") {
|
||||||
|
throw new TypeError("content-type header is missing from object");
|
||||||
|
}
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
var ContentType = class {
|
||||||
|
parameters;
|
||||||
|
type;
|
||||||
|
constructor(type) {
|
||||||
|
this.parameters = {};
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function format(obj) {
|
||||||
|
if (!obj || typeof obj !== "object")
|
||||||
|
throw new TypeError("argument obj is required");
|
||||||
|
const { parameters, type } = obj;
|
||||||
|
if (!type || !TYPE_REGEXP.test(type))
|
||||||
|
throw new TypeError("invalid type");
|
||||||
|
let string = type;
|
||||||
|
if (parameters && typeof parameters == "object") {
|
||||||
|
const params = Object.keys(parameters).sort();
|
||||||
|
for (const param of params) {
|
||||||
|
if (!TOKEN_REGEXP.test(param))
|
||||||
|
throw new TypeError("invalid parameter name");
|
||||||
|
string += "; " + param + "=" + qstring(parameters[param]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
function parse(string) {
|
||||||
|
if (!string)
|
||||||
|
throw new TypeError("argument string is required");
|
||||||
|
const header = typeof string == "object" ? getcontenttype(string) : string;
|
||||||
|
if (typeof header !== "string")
|
||||||
|
throw new TypeError("argument string is required to be a string");
|
||||||
|
let index = header.indexOf(";");
|
||||||
|
const type = index != -1 ? header.slice(0, index).trim() : header.trim();
|
||||||
|
if (!TYPE_REGEXP.test(type))
|
||||||
|
throw new TypeError("invalid media type");
|
||||||
|
const obj = new ContentType(type.toLowerCase());
|
||||||
|
if (index != -1) {
|
||||||
|
let key;
|
||||||
|
let match;
|
||||||
|
let value;
|
||||||
|
PARAM_REGEXP.lastIndex = index;
|
||||||
|
while (match = PARAM_REGEXP.exec(header)) {
|
||||||
|
if (match.index !== index)
|
||||||
|
throw new TypeError("invalid parameter format");
|
||||||
|
index += match[0].length;
|
||||||
|
key = match[1].toLowerCase();
|
||||||
|
value = match[2];
|
||||||
|
if (value[0] == '"') {
|
||||||
|
value = value.slice(1, value.length - 1).replace(QESC_REGEXP, "$1");
|
||||||
|
}
|
||||||
|
obj.parameters[key] = value;
|
||||||
|
}
|
||||||
|
if (index != header.length)
|
||||||
|
throw new TypeError("invalid parameter format");
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
format,
|
||||||
|
parse
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/content-type",
|
||||||
|
"description": "content-type rewrite in TypeScript and ESM",
|
||||||
|
"version": "0.1.4",
|
||||||
|
"repository": "https://github.com/tinyhttp/content-type.git",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.4"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "v1rtl <hi@v1rtl.site>",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.6.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||||
|
"@typescript-eslint/parser": "^6.7.2",
|
||||||
|
"c8": "^8.0.1",
|
||||||
|
"eslint": "^8.50.0",
|
||||||
|
"tsm": "^2.3.0",
|
||||||
|
"tsup": "^7.2.0",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"uvu": "^0.5.6"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup src/index.ts --format esm --dts",
|
||||||
|
"test": "uvu -r tsm test",
|
||||||
|
"test:coverage": "c8 --include=src pnpm test",
|
||||||
|
"test:report": "c8 report --reporter=text-lcov > coverage.lcov"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,25 @@
|
||||||
|
# @tinyhttp/cookie-signature
|
||||||
|
|
||||||
|
[](https://npmjs.com/package/@tinyhttp/cookie-signature) [](https://npmjs.com/package/@tinyhttp/cookie-signature)
|
||||||
|
|
||||||
|
HTTP cookie signing and unsigning. A rewrite of [cookie-signature](https://github.com/tj/node-cookie-signature) module.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/cookie-signature
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { sign, unsign } from '@tinyhttp/cookie-signature'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `sign(val, secret)`
|
||||||
|
|
||||||
|
Signd the given `val` with `secret`.
|
||||||
|
|
||||||
|
### `unsign(val, secret)`
|
||||||
|
|
||||||
|
Unsign and decode the given `val` with `secret`, returning `false` if the signature is invalid.
|
10
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.d.ts
generated
vendored
Normal file
10
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Sign the given `val` with `secret`.
|
||||||
|
*/
|
||||||
|
export declare const sign: (val: string, secret: string) => string;
|
||||||
|
/**
|
||||||
|
* Unsign and decode the given `val` with `secret`,
|
||||||
|
* returning `false` if the signature is invalid.
|
||||||
|
*/
|
||||||
|
export declare const unsign: (val: string, secret: string) => string | false;
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
1
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.d.ts.map
generated
vendored
Normal file
1
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.d.ts.map
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,IAAI,QAAS,MAAM,UAAU,MAAM,KAAG,MACuC,CAAA;AAE1F;;;GAGG;AACH,eAAO,MAAM,MAAM,QAAS,MAAM,UAAU,MAAM,KAAG,MAAM,GAAG,KAQ7D,CAAA"}
|
11
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.js
generated
vendored
Normal file
11
comments/Home/node_modules/@tinyhttp/cookie-signature/dist/index.js
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
||||||
|
const sign = (val, secret) => `${val}.${createHmac("sha256", secret).update(val).digest("base64").replace(/=+$/, "")}`;
|
||||||
|
const unsign = (val, secret) => {
|
||||||
|
const str = val.slice(0, val.lastIndexOf(".")), mac = sign(str, secret), macBuffer = Buffer.from(mac), valBuffer = Buffer.alloc(macBuffer.length);
|
||||||
|
valBuffer.write(val);
|
||||||
|
return timingSafeEqual(macBuffer, valBuffer) ? str : false;
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
sign,
|
||||||
|
unsign
|
||||||
|
};
|
36
comments/Home/node_modules/@tinyhttp/cookie-signature/package.json
generated
vendored
Normal file
36
comments/Home/node_modules/@tinyhttp/cookie-signature/package.json
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/cookie-signature",
|
||||||
|
"version": "2.1.0",
|
||||||
|
"description": "HTTP cookie signing and unsigning",
|
||||||
|
"homepage": "https://tinyhttp.v1rtl.site",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/cookie-signature"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"keywords": [
|
||||||
|
"tinyhttp",
|
||||||
|
"node.js",
|
||||||
|
"web framework",
|
||||||
|
"web",
|
||||||
|
"backend",
|
||||||
|
"static",
|
||||||
|
"cookie"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "v1rtl",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,176 @@
|
||||||
|
# @tinyhttp/cookie
|
||||||
|
|
||||||
|
[](https://npmjs.com/package/@tinyhttp/cookie) [](https://npmjs.com/package/@tinyhttp/cookie)
|
||||||
|
|
||||||
|
> A rewrite of [cookie](https://github.com/jshttp/cookie) module.
|
||||||
|
|
||||||
|
HTTP cookie parser and serializer for Node.js.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/cookie
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { parse, serialize } from '@tinyhttp/cookie'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `parse(str, options)`
|
||||||
|
|
||||||
|
Parse an HTTP `Cookie` header string and returning an object of all cookie name-value pairs.
|
||||||
|
The `str` argument is the string representing a `Cookie` header value and `options` is an
|
||||||
|
optional object containing additional parsing options.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { parse } from '@tinyhttp/cookie'
|
||||||
|
|
||||||
|
parse('foo=bar; equation=E%3Dmc%5E2')
|
||||||
|
// { foo: 'bar', equation: 'E=mc^2' }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
`parse` accepts these properties in the options object.
|
||||||
|
|
||||||
|
##### `decode`
|
||||||
|
|
||||||
|
Specifies a function that will be used to decode a cookie's value. Since the value of a cookie
|
||||||
|
has a limited character set (and must be a simple string), this function can be used to decode
|
||||||
|
a previously-encoded cookie value into a JavaScript string or other object.
|
||||||
|
|
||||||
|
The default function is the global `decodeURIComponent`, which will decode any URL-encoded
|
||||||
|
sequences into their byte representations.
|
||||||
|
|
||||||
|
**note** if an error is thrown from this function, the original, non-decoded cookie value will
|
||||||
|
be returned as the cookie's value.
|
||||||
|
|
||||||
|
### `serialize(name, value, options)`
|
||||||
|
|
||||||
|
Serialize a cookie name-value pair into a `Set-Cookie` header string. The `name` argument is the
|
||||||
|
name for the cookie, the `value` argument is the value to set the cookie to, and the `options`
|
||||||
|
argument is an optional object containing additional serialization options.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { serialize } from '@tinyhttp/cookie'
|
||||||
|
|
||||||
|
serialize('foo', 'bar')
|
||||||
|
// foo=bar
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
`serialize` accepts these properties in the options object.
|
||||||
|
|
||||||
|
##### `domain`
|
||||||
|
|
||||||
|
Specifies the value for the [`Domain` `Set-Cookie` attribute][rfc-6265-5.2.3]. By default, no
|
||||||
|
domain is set, and most clients will consider the cookie to apply to only the current domain.
|
||||||
|
|
||||||
|
##### `encode`
|
||||||
|
|
||||||
|
Specifies a function that will be used to encode a cookie's value. Since value of a cookie
|
||||||
|
has a limited character set (and must be a simple string), this function can be used to encode
|
||||||
|
a value into a string suited for a cookie's value.
|
||||||
|
|
||||||
|
The default function is the global `encodeURIComponent`, which will encode a JavaScript string
|
||||||
|
into UTF-8 byte sequences and then URL-encode any that fall outside of the cookie range.
|
||||||
|
|
||||||
|
##### `expires`
|
||||||
|
|
||||||
|
Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute][rfc-6265-5.2.1].
|
||||||
|
By default, no expiration is set, and most clients will consider this a "non-persistent cookie" and
|
||||||
|
will delete it on a condition like exiting a web browser application.
|
||||||
|
|
||||||
|
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
||||||
|
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
||||||
|
so if both are set, they should point to the same date and time.
|
||||||
|
|
||||||
|
##### `httpOnly`
|
||||||
|
|
||||||
|
Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute][rfc-6265-5.2.6]. When truthy,
|
||||||
|
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly` attribute is not set.
|
||||||
|
|
||||||
|
**note** be careful when setting this to `true`, as compliant clients will not allow client-side
|
||||||
|
JavaScript to see the cookie in `document.cookie`.
|
||||||
|
|
||||||
|
##### `maxAge`
|
||||||
|
|
||||||
|
Specifies the `number` (in seconds) to be the value for the [`Max-Age` `Set-Cookie` attribute][rfc-6265-5.2.2].
|
||||||
|
The given number will be converted to an integer by rounding down. By default, no maximum age is set.
|
||||||
|
|
||||||
|
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
||||||
|
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
||||||
|
so if both are set, they should point to the same date and time.
|
||||||
|
|
||||||
|
##### `path`
|
||||||
|
|
||||||
|
Specifies the value for the [`Path` `Set-Cookie` attribute][rfc-6265-5.2.4]. By default, the path
|
||||||
|
is considered the ["default path"][rfc-6265-5.1.4].
|
||||||
|
|
||||||
|
##### `sameSite`
|
||||||
|
|
||||||
|
Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute][rfc-6265bis-03-4.1.2.7].
|
||||||
|
|
||||||
|
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||||
|
- `false` will not set the `SameSite` attribute.
|
||||||
|
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
||||||
|
- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
|
||||||
|
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||||
|
|
||||||
|
More information about the different enforcement levels can be found in
|
||||||
|
[the specification][rfc-6265bis-03-4.1.2.7].
|
||||||
|
|
||||||
|
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||||
|
This also means many clients may ignore this attribute until they understand it.
|
||||||
|
|
||||||
|
##### `secure`
|
||||||
|
|
||||||
|
Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute][rfc-6265-5.2.5]. When truthy,
|
||||||
|
the `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
||||||
|
|
||||||
|
**note** be careful when setting this to `true`, as compliant clients will not send the cookie back to
|
||||||
|
the server in the future if the browser does not have an HTTPS connection.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { App } from '@tinyhttp/app'
|
||||||
|
import { parse, serialize } from '@tinyhttp/cookie'
|
||||||
|
import { escapeHTML } from 'es-escape-html'
|
||||||
|
|
||||||
|
new App()
|
||||||
|
.use((req, res) => {
|
||||||
|
if (req.query?.name) {
|
||||||
|
// Set a new cookie with the name
|
||||||
|
res.set(
|
||||||
|
'Set-Cookie',
|
||||||
|
serialize('name', String(query.name), {
|
||||||
|
httpOnly: true,
|
||||||
|
maxAge: 60 * 60 * 24 * 7 // 1 week
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redirect back after setting cookie
|
||||||
|
res
|
||||||
|
.status(302)
|
||||||
|
.set('Location', req.headers.referer || '/')
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookie = parse(req.headers.cookie || '')
|
||||||
|
|
||||||
|
const { name } = cookie
|
||||||
|
|
||||||
|
res.set('Content-Type', 'text/html; charset=UTF-8')
|
||||||
|
|
||||||
|
res.write(name ? `<p>Welcome back, <strong>${escapeHTML(name)}</strong>!</p>` : '<p>Hello, new visitor!</p>')
|
||||||
|
|
||||||
|
res.write('<form method="GET">')
|
||||||
|
res.write('<input placeholder="enter your name" name="name"><input type="submit" value="Set Name">')
|
||||||
|
res.end('</form>')
|
||||||
|
})
|
||||||
|
.listen(3000)
|
||||||
|
```
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Parse a cookie header.
|
||||||
|
*
|
||||||
|
* Parse the given cookie header string into an object
|
||||||
|
* The object has the various cookies as keys(names) => values
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export declare function parse(str: string, options?: {
|
||||||
|
decode: (str: string) => string;
|
||||||
|
}): Record<string, string>;
|
||||||
|
export type SerializeOptions = Partial<{
|
||||||
|
encode: (str: string) => string;
|
||||||
|
maxAge: number;
|
||||||
|
domain: string;
|
||||||
|
path: string;
|
||||||
|
httpOnly: boolean;
|
||||||
|
secure: boolean;
|
||||||
|
sameSite: boolean | 'Strict' | 'strict' | 'Lax' | 'lax' | 'None' | 'none' | string;
|
||||||
|
expires: Date;
|
||||||
|
}>;
|
||||||
|
export declare function serialize(name: string, val: string, opt?: SerializeOptions): string;
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IACP,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAGhC,GACA,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBxB;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC;IACrC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IAClF,OAAO,EAAE,IAAI,CAAA;CACd,CAAC,CAAA;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,gBAAqB,GAAG,MAAM,CAyDvF"}
|
|
@ -0,0 +1,81 @@
|
||||||
|
const pairSplitRegExp = /; */;
|
||||||
|
const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
||||||
|
function tryDecode(str, decode) {
|
||||||
|
try {
|
||||||
|
return decode(str);
|
||||||
|
} catch (e) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function parse(str, options = {
|
||||||
|
decode: decodeURIComponent
|
||||||
|
}) {
|
||||||
|
const obj = {};
|
||||||
|
const pairs = str.split(pairSplitRegExp);
|
||||||
|
for (const pair of pairs) {
|
||||||
|
let eqIdx = pair.indexOf("=");
|
||||||
|
if (eqIdx < 0)
|
||||||
|
continue;
|
||||||
|
const key = pair.substr(0, eqIdx).trim();
|
||||||
|
let val = pair.substr(++eqIdx, pair.length).trim();
|
||||||
|
if ('"' == val[0])
|
||||||
|
val = val.slice(1, -1);
|
||||||
|
if (obj[key] == null)
|
||||||
|
obj[key] = tryDecode(val, options.decode);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
function serialize(name, val, opt = {}) {
|
||||||
|
if (!opt.encode)
|
||||||
|
opt.encode = encodeURIComponent;
|
||||||
|
if (!fieldContentRegExp.test(name))
|
||||||
|
throw new TypeError("argument name is invalid");
|
||||||
|
const value = opt.encode(val);
|
||||||
|
if (value && !fieldContentRegExp.test(value))
|
||||||
|
throw new TypeError("argument val is invalid");
|
||||||
|
let str = name + "=" + value;
|
||||||
|
if (null != opt.maxAge) {
|
||||||
|
const maxAge = opt.maxAge - 0;
|
||||||
|
if (isNaN(maxAge) || !isFinite(maxAge))
|
||||||
|
throw new TypeError("option maxAge is invalid");
|
||||||
|
str += "; Max-Age=" + Math.floor(maxAge);
|
||||||
|
}
|
||||||
|
if (opt.domain) {
|
||||||
|
if (!fieldContentRegExp.test(opt.domain))
|
||||||
|
throw new TypeError("option domain is invalid");
|
||||||
|
str += "; Domain=" + opt.domain;
|
||||||
|
}
|
||||||
|
if (opt.path) {
|
||||||
|
if (!fieldContentRegExp.test(opt.path))
|
||||||
|
throw new TypeError("option path is invalid");
|
||||||
|
str += "; Path=" + opt.path;
|
||||||
|
}
|
||||||
|
if (opt.expires)
|
||||||
|
str += "; Expires=" + opt.expires.toUTCString();
|
||||||
|
if (opt.httpOnly)
|
||||||
|
str += "; HttpOnly";
|
||||||
|
if (opt.secure)
|
||||||
|
str += "; Secure";
|
||||||
|
if (opt.sameSite) {
|
||||||
|
const sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
|
||||||
|
switch (sameSite) {
|
||||||
|
case true:
|
||||||
|
case "strict":
|
||||||
|
str += "; SameSite=Strict";
|
||||||
|
break;
|
||||||
|
case "lax":
|
||||||
|
str += "; SameSite=Lax";
|
||||||
|
break;
|
||||||
|
case "none":
|
||||||
|
str += "; SameSite=None";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new TypeError("option sameSite is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
parse,
|
||||||
|
serialize
|
||||||
|
};
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/cookie",
|
||||||
|
"version": "2.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "HTTP cookie parser and serializer for Node.js",
|
||||||
|
"homepage": "https://github.com/tinyhttp/tinyhttp/tree/master/packages/cookie#readme",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/cookie"
|
||||||
|
},
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"keywords": [
|
||||||
|
"tinyhttp",
|
||||||
|
"node.js",
|
||||||
|
"web framework",
|
||||||
|
"web",
|
||||||
|
"backend",
|
||||||
|
"cookie"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "v1rtl",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,73 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
# @tinyhttp/cors
|
||||||
|
|
||||||
|
[![npm][npm-img]][npm-url] [![GitHub Workflow Status][gh-actions-img]][github-actions] [![Coverage][cov-img]][cov-url]
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
> A rewrite of [expressjs/cors](https://github.com/expressjs/cors) module.
|
||||||
|
|
||||||
|
HTTP cors header middleware.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/cors
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { cors } from '@tinyhttp/cors'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `cors(options)`
|
||||||
|
|
||||||
|
Returns the CORS middleware with the settings specified in the parameters
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
- `origin`: Can be a string defining the `Access-Control-Allow-Origin` value, a boolean which if set to true sets the header to `'*'`, a Regex type, an array (for multiple origins) or a function which contains the request and response as parameters and must return the value for the `Access-Control-Allow-Origin` header
|
||||||
|
- `methods`: Array of method names which define the `Access-Control-Allow-Methods` header, default to all the most common methods (`GET`, `HEAD`, `PUT`, `PATCH`, `POST`, `DELETE`)
|
||||||
|
- `allowedHeaders`: Configures the `Access-Control-Allow-Headers` CORS header. Expects an array (ex: [`'Content-Type'`, `'Authorization'`]).
|
||||||
|
- `exposedHeaders`: Configures the `Access-Control-Expose-Headers` CORS header. If not specified, no custom headers are exposed
|
||||||
|
- `credentials`: Configures the `Access-Control-Allow-Credentials` CORS header. Set to true to pass the header, otherwise it is omitted.
|
||||||
|
- `maxAge`: Configures the `Access-Control-Max-Age` CORS header. Set to an integer to pass the header, otherwise it is omitted.
|
||||||
|
- `optionsSuccessStatus`: Provides a status code to use for successful OPTIONS requests, since some legacy browsers (IE11, various SmartTVs) choke on 204.
|
||||||
|
- `preflightContinue`: Set 204 and finish response if `true`, call `next` if false.
|
||||||
|
|
||||||
|
The default configuration is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"origin": "*",
|
||||||
|
"methods": ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"],
|
||||||
|
"optionsSuccessStatus": 204,
|
||||||
|
"preflightContinue": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { App } from '@tinyhttp/app'
|
||||||
|
import { cors } from '@tinyhttp/cors'
|
||||||
|
|
||||||
|
const app = new App()
|
||||||
|
|
||||||
|
app
|
||||||
|
.use(cors({ origin: 'https://myfantastic.site/' }))
|
||||||
|
.options('*', cors())
|
||||||
|
.get('/', (req, res) => {
|
||||||
|
res.send('The headers contained in my response are defined in the cors middleware')
|
||||||
|
})
|
||||||
|
.listen(3000)
|
||||||
|
```
|
||||||
|
|
||||||
|
[npm-url]: https://npmjs.com/package/@tinyhttp/cors
|
||||||
|
[github-actions]: https://github.com/tinyhttp/cors/actions
|
||||||
|
[gh-actions-img]: https://img.shields.io/github/workflow/status/tinyhttp/cors/CI?style=for-the-badge&logo=github&label=&color=hotpink
|
||||||
|
[cov-img]: https://img.shields.io/coveralls/github/tinyhttp/cors?style=for-the-badge&color=hotpink
|
||||||
|
[cov-url]: https://coveralls.io/github/tinyhttp/cors
|
||||||
|
[npm-img]: https://img.shields.io/npm/dt/@tinyhttp/cors?style=for-the-badge&color=hotpink
|
|
@ -0,0 +1,16 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { IncomingMessage as Request, ServerResponse as Response } from 'http';
|
||||||
|
export interface AccessControlOptions {
|
||||||
|
origin?: string | boolean | ((req: Request, res: Response) => string) | Array<string> | RegExp;
|
||||||
|
methods?: string[];
|
||||||
|
allowedHeaders?: string[];
|
||||||
|
exposedHeaders?: string[];
|
||||||
|
credentials?: boolean;
|
||||||
|
maxAge?: number;
|
||||||
|
optionsSuccessStatus?: number;
|
||||||
|
preflightContinue?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* CORS Middleware
|
||||||
|
*/
|
||||||
|
export declare const cors: (opts?: AccessControlOptions) => (req: Request, res: Response, next?: () => void) => void;
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { vary } from 'es-vary';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CORS Middleware
|
||||||
|
*/
|
||||||
|
const cors = (opts = {}) => {
|
||||||
|
const { origin = '*', methods = ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'], allowedHeaders = ['content-type'], exposedHeaders, credentials, maxAge, optionsSuccessStatus = 204, preflightContinue = false } = opts;
|
||||||
|
return (req, res, next) => {
|
||||||
|
var _a, _b;
|
||||||
|
// Checking the type of the origin property
|
||||||
|
if (typeof origin === 'boolean' && origin === true) {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
}
|
||||||
|
else if (typeof origin === 'string') {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||||
|
}
|
||||||
|
else if (typeof origin === 'function') {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', origin(req, res));
|
||||||
|
}
|
||||||
|
else if (typeof origin === 'object') {
|
||||||
|
if (Array.isArray(origin) && (origin.indexOf(req.headers.origin) !== -1 || origin.indexOf('*') !== -1)) {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
|
||||||
|
}
|
||||||
|
else if (origin instanceof RegExp && origin.test(req.headers.origin)) {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new TypeError('No other objects allowed. Allowed types is array of strings or RegExp');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((typeof origin === 'string' && origin !== '*') || typeof origin === 'function')
|
||||||
|
vary(res, 'Origin');
|
||||||
|
// Setting the Access-Control-Allow-Methods header from the methods array
|
||||||
|
res.setHeader('Access-Control-Allow-Methods', methods.join(', ').toUpperCase());
|
||||||
|
// Setting the Access-Control-Allow-Headers header
|
||||||
|
if (allowedHeaders)
|
||||||
|
res.setHeader('Access-Control-Allow-Headers', allowedHeaders);
|
||||||
|
// Setting the Access-Control-Expose-Headers header
|
||||||
|
if (exposedHeaders)
|
||||||
|
res.setHeader('Access-Control-Expose-Headers', exposedHeaders);
|
||||||
|
// Setting the Access-Control-Allow-Credentials header
|
||||||
|
if (credentials)
|
||||||
|
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
||||||
|
// Setting the Access-Control-Max-Age header
|
||||||
|
if (maxAge)
|
||||||
|
res.setHeader('Access-Control-Max-Age', maxAge);
|
||||||
|
if (((_b = (_a = req.method) === null || _a === void 0 ? void 0 : _a.toUpperCase) === null || _b === void 0 ? void 0 : _b.call(_a)) === 'OPTIONS') {
|
||||||
|
if (preflightContinue) {
|
||||||
|
next === null || next === void 0 ? void 0 : next();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.statusCode = optionsSuccessStatus;
|
||||||
|
res.setHeader('Content-Length', '0');
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
next === null || next === void 0 ? void 0 : next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export { cors };
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/cors",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "CORS middleware for modern Node.js ",
|
||||||
|
"homepage": "https://github.com/tinyhttp/cors#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/cors.git"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.4 || 14.x || >=16"
|
||||||
|
},
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"keywords": [
|
||||||
|
"tinyhttp",
|
||||||
|
"node.js",
|
||||||
|
"web framework",
|
||||||
|
"web",
|
||||||
|
"backend"
|
||||||
|
],
|
||||||
|
"author": "v1rtl",
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"es-vary": "^0.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "13.1.0",
|
||||||
|
"@commitlint/config-conventional": "13.1.0",
|
||||||
|
"@rollup/plugin-typescript": "^8.2.5",
|
||||||
|
"@tinyhttp/app": "1.3.15",
|
||||||
|
"@types/node": "^16.7.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.29.2",
|
||||||
|
"@typescript-eslint/parser": "^4.29.2",
|
||||||
|
"c8": "^7.8.0",
|
||||||
|
"esbuild-node-loader": "^0.3.1",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^3.4.1",
|
||||||
|
"expect": "^27.0.6",
|
||||||
|
"husky": "^7.0.1",
|
||||||
|
"prettier": "^2.3.2",
|
||||||
|
"rollup": "^2.56.2",
|
||||||
|
"supertest-fetch": "^1.4.3",
|
||||||
|
"typescript": "^4.3.5",
|
||||||
|
"uvu": "^0.5.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c",
|
||||||
|
"test": "node --experimental-loader esbuild-node-loader node_modules/uvu/bin.js tests",
|
||||||
|
"test:coverage": "c8 --include=src pnpm test",
|
||||||
|
"test:report": "c8 report --reporter=text-lcov > coverage.lcov",
|
||||||
|
"lint": "eslint . --ext=ts",
|
||||||
|
"format": "prettier --check \"./**/*.{ts,md}\"",
|
||||||
|
"format:fix": "prettier --write \"./**/*.{ts,md}\""
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,15 @@
|
||||||
|
# @tinyhttp/encode-url
|
||||||
|
|
||||||
|
> [`encode-url`](https://github.com/pillarjs/encodeurl) rewrite in TypeScript.
|
||||||
|
|
||||||
|
Encode a URL to a percent-encoded form, excluding already-encoded sequences
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/encode-url
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
## Example
|
|
@ -0,0 +1,2 @@
|
||||||
|
export declare const encodeUrl: (url: string) => string;
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
1
comments/Home/node_modules/@tinyhttp/encode-url/dist/index.d.ts.map
generated
vendored
Normal file
1
comments/Home/node_modules/@tinyhttp/encode-url/dist/index.d.ts.map
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,SAAS,QAAS,MAAM,KAAG,MAIvC,CAAA"}
|
|
@ -0,0 +1,10 @@
|
||||||
|
const ENCODE_CHARS_REGEXP = /(?:[^\x21\x25\x26-\x3B\x3D\x3F-\x5B\x5D\x5F\x61-\x7A\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g;
|
||||||
|
const UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g;
|
||||||
|
const UNMATCHED_SURROGATE_PAIR_REPLACE = "$1<>$2";
|
||||||
|
const encodeUrl = (url) => {
|
||||||
|
return String(url).replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE).replace(ENCODE_CHARS_REGEXP, encodeURI);
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
encodeUrl
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["const ENCODE_CHARS_REGEXP =\n /(?:[^\\x21\\x25\\x26-\\x3B\\x3D\\x3F-\\x5B\\x5D\\x5F\\x61-\\x7A\\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g\n\nconst UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF]([^\\uDC00-\\uDFFF]|$)/g\n\nconst UNMATCHED_SURROGATE_PAIR_REPLACE = '$1\\uFFFD$2'\n\nexport const encodeUrl = (url: string): string => {\n return String(url)\n .replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE)\n .replace(ENCODE_CHARS_REGEXP, encodeURI)\n}\n"],"names":[],"mappings":"AAAA,MAAM,sBACJ;AAEF,MAAM,kCAAkC;AAExC,MAAM,mCAAmC;AAE5B,MAAA,YAAY,CAAC,QAAwB;AACzC,SAAA,OAAO,GAAG,EACd,QAAQ,iCAAiC,gCAAgC,EACzE,QAAQ,qBAAqB,SAAS;AAC3C;"}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "@tinyhttp/encode-url",
|
||||||
|
"version": "2.1.1",
|
||||||
|
"description": "encode-url rewrite in TypeScript",
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"homepage": "https://tinyhttp.v1rtl.site",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tinyhttp/tinyhttp.git",
|
||||||
|
"directory": "packages/cors"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tinyhttp",
|
||||||
|
"node.js",
|
||||||
|
"web framework",
|
||||||
|
"web",
|
||||||
|
"backend",
|
||||||
|
"cors"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
},
|
||||||
|
"author": "v1rtl",
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 v 1 r t l
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,35 @@
|
||||||
|
# @tinyhttp/etag
|
||||||
|
|
||||||
|
[](https://npmjs.com/package/@tinyhttp/etag) [](https://npmjs.com/package/@tinyhttp/etag) [](https://tinyhttp.v1rtl.site/mw/etag)
|
||||||
|
|
||||||
|
> A rewrite of [etag](https://www.npmjs.com/package/etag) module.
|
||||||
|
|
||||||
|
This module generates HTTP ETags (as defined in RFC 7232) for use in HTTP responses.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm i @tinyhttp/etag
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { eTag } from '@tinyhttp/etag'
|
||||||
|
```
|
||||||
|
|
||||||
|
`eTag(entity, [options])`
|
||||||
|
|
||||||
|
Generate a strong ETag for the given entity. This should be the complete body of the entity. Strings, `Buffer`s, and `fs.Stats` are accepted. By default, a strong ETag is generated except for `fs.Stats`, which will generate a weak ETag (this can be overwritten by options.weak).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
res.setHeader('ETag', eTag(body))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
`eTag` accepts these properties in the options object.
|
||||||
|
|
||||||
|
#### `weak`
|
||||||
|
|
||||||
|
Specifies if the generated ETag will include the weak validator mark (that is, the leading `W/`). The actual entity tag is the same. The default value is `false`, unless the entity is `fs.Stats`, in which case it is `true`.
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// <reference types="node" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { Stats } from 'node:fs';
|
||||||
|
export declare const eTag: (entity: string | Buffer | Stats, options?: {
|
||||||
|
weak: boolean;
|
||||||
|
}) => string;
|
||||||
|
//# sourceMappingURL=index.d.ts.map
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAuB/B,eAAO,MAAM,IAAI,WAAY,MAAM,GAAG,MAAM,GAAG,KAAK,YAAY;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,KAAG,MAUnF,CAAA"}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createHash } from "node:crypto";
|
||||||
|
import { Stats } from "node:fs";
|
||||||
|
const entityTag = (entity) => {
|
||||||
|
if (entity.length === 0) {
|
||||||
|
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
||||||
|
} else {
|
||||||
|
const hash = createHash("sha1").update(entity, "utf8").digest("base64").substring(0, 27);
|
||||||
|
const len = typeof entity === "string" ? Buffer.byteLength(entity, "utf8") : entity.length;
|
||||||
|
return '"' + len.toString(16) + "-" + hash + '"';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const statTag = ({ mtime, size }) => {
|
||||||
|
return '"' + mtime.getTime().toString(16) + "-" + size.toString(16) + '"';
|
||||||
|
};
|
||||||
|
const eTag = (entity, options) => {
|
||||||
|
if (entity == null)
|
||||||
|
throw new TypeError("argument entity is required");
|
||||||
|
const weak = (options == null ? void 0 : options.weak) || entity instanceof Stats;
|
||||||
|
const tag = entity instanceof Stats ? statTag(entity) : entityTag(entity);
|
||||||
|
return weak ? "W/" + tag : tag;
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
eTag
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=index.js.map
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue