Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
out
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the "hanzo" extension will be documented in this file.
|
||||
|
||||
## [1.5.4] - 2025-03-17
|
||||
|
||||
### Added
|
||||
- Centralized text content management through JSON files for easier customization
|
||||
- Enhanced welcome view with larger, centered text focusing on main benefits
|
||||
- Simplified onboarding experience with focus on core value proposition
|
||||
- Enhanced status bar indicator for non-logged in users with "Hanzo: Login" text
|
||||
- Improved visual styling and typography throughout the onboarding experience
|
||||
|
||||
## [1.5.3] - 2025-03-17
|
||||
|
||||
### Added
|
||||
- Enhanced metrics visibility with automatic display after first analysis
|
||||
|
||||
## [1.5.2] - 2025-03-14
|
||||
|
||||
### Added
|
||||
- Converted refinement process into multi-step API calls
|
||||
- Added detailed logging for API responses to improve debugging
|
||||
|
||||
## [1.5.1] - 2025-03-13
|
||||
|
||||
### Added
|
||||
- Increased global API request timeout to 300 seconds (5 minutes) for better handling of large projects
|
||||
- Added retry mechanism for specification refinement to improve reliability
|
||||
- Enhanced error handling for timeout errors with more informative messages
|
||||
|
||||
### Changed
|
||||
- Improved logging during refinement process with detailed timing information
|
||||
- Better user feedback during refinement operations
|
||||
|
||||
## [1.5.0] - 2025-03-13
|
||||
|
||||
### Added
|
||||
- Multi-file specifications
|
||||
- IDE-specific organization of specification files:
|
||||
- For Cursor: Main overview in `.cursorrules` and additional files in `.cursor/rules/`
|
||||
- For other IDEs: Main overview in IDE-specific file and additional files in `.hanzo/rules/`
|
||||
|
||||
### Changed
|
||||
- Removed the SPEC.md file approach in favor of IDE-specific rules files
|
||||
|
||||
## [1.4.0] - 2025-03-13
|
||||
|
||||
### Added
|
||||
- Enhanced UI with improved progress indicators and responsive feedback
|
||||
- Optimized chunking system with 4MB chunk size for better performance
|
||||
- Improved error handling and user feedback during large file uploads
|
||||
|
||||
## [1.3.1] - 2025-03-13
|
||||
|
||||
### Added
|
||||
- Implemented gzip compression for API requests to reduce data transfer size
|
||||
- Enhanced chunking algorithm with intelligent compression ratio analysis
|
||||
- Improved handling of large projects with adaptive chunk sizing
|
||||
- Added detailed logging for upload process monitoring
|
||||
|
||||
## [1.3.0] - 2025-03-13
|
||||
|
||||
### Added
|
||||
- Enhanced upload reliability for projects of all sizes with optimized chunking algorithm
|
||||
|
||||
## [1.2.9] - 2025-03-12
|
||||
|
||||
### Added
|
||||
- Implemented chunking for large file uploads to handle projects of any size
|
||||
- Improved request handling for large codebases by splitting files into manageable chunks
|
||||
|
||||
## [1.2.8] - 2025-03-09
|
||||
|
||||
### Changed
|
||||
- Update readme
|
||||
|
||||
## [1.2.7] - 2025-03-09
|
||||
|
||||
### Fixed
|
||||
- Add login wall
|
||||
|
||||
## [1.2.6] - 2025-03-08
|
||||
|
||||
### Changed
|
||||
- Reduced frequency of reanalysis reminders to be less intrusive
|
||||
|
||||
## [1.2.5] - 2025-02-21
|
||||
|
||||
### Added
|
||||
- Support for Codium (.windsurfrules)
|
||||
|
||||
## [1.2.4] - 2025-02-21
|
||||
|
||||
### Changed
|
||||
- Extension name
|
||||
|
||||
## [1.2.3] - 2025-02-16
|
||||
|
||||
### Fixed
|
||||
- Fixed "webview is disposed" error during analysis operations by making UI updates optional
|
||||
|
||||
## [1.2.2] - 2025-02-16
|
||||
|
||||
### Added
|
||||
- Support for Continue (.continuerules)
|
||||
|
||||
## [1.2.1] - 2025-02-16
|
||||
|
||||
### Fixed
|
||||
- Improved Git extension activation handling for better stability
|
||||
- Enhanced error logging for API requests
|
||||
- Fixed production initialization issues
|
||||
|
||||
## [1.2.0] - 2025-02-16
|
||||
|
||||
### Added
|
||||
- Add authentication
|
||||
- Comprehensive test suite for authentication and API functionality
|
||||
- Add reminders
|
||||
|
||||
## [1.1.13] - 2025-01-31
|
||||
|
||||
### Changed
|
||||
- Replaced native fetch with axios for improved HTTP request handling
|
||||
- Enhanced error handling with better TypeScript support
|
||||
- Improved API communication reliability
|
||||
- Added better error messages with response data
|
||||
- Streamlined response processing with automatic JSON parsing
|
||||
|
||||
## [1.1.11] - 2025-01-30
|
||||
|
||||
### Changed
|
||||
- Enhanced file collection logging with detailed tree structure output
|
||||
- Improved directory scanning with better error handling and progress tracking
|
||||
- Added comprehensive logging of included and ignored files for better debugging
|
||||
|
||||
## [1.1.10] - 2025-01-22
|
||||
|
||||
### Changed
|
||||
- Improved reanalysis UX with minimal loading indicators
|
||||
- Added status bar progress indicator
|
||||
- Enhanced progress tracking with detailed stages
|
||||
- Prevented webview from opening during quick reanalysis
|
||||
- Made reanalysis more streamlined when triggered via command palette or editor icon
|
||||
|
||||
## [1.1.9] - 2025-01-20
|
||||
|
||||
### Changed
|
||||
- Enhanced directory size reporting with file counts
|
||||
- Improved logging with consistent [Hanzo] prefix
|
||||
- Better error handling with appropriate log levels
|
||||
|
||||
## [1.1.8] - 2025-01-18
|
||||
|
||||
### Changed
|
||||
- Ignore additional folders relating to testing, CI/CD, and other non-source code files
|
||||
|
||||
## [1.1.7] - 2025-01-16
|
||||
|
||||
### Changed
|
||||
- Allow choice of IDE between Cursor and Copilot
|
||||
- Ignore additional folders relating to testing, CI/CD, and other non-source code files
|
||||
|
||||
## [1.1.6] - 2025-01-13
|
||||
|
||||
### Added
|
||||
- Quick access to "Reanalyze" via command palette
|
||||
- Always-visible reanalyze button in editor toolbar
|
||||
- Keyboard shortcut support for reanalyzing projects
|
||||
|
||||
### Changed
|
||||
- Improved project reanalysis workflow with better UI feedback
|
||||
- Enhanced status updates during reanalysis
|
||||
|
||||
## [1.1.5] - 2025-01-05
|
||||
|
||||
### Added
|
||||
- Directory size tracking and reporting for better debugging (now excludes ignored files)
|
||||
- Additional ignored patterns for common build and module folders:
|
||||
- Virtual environments: `.venv`, `venv`, `env`, `.env`
|
||||
- Python cache: `__pycache__`, `.pyc` files
|
||||
- Common build folders: `out`, `target`, `bin`, `obj`, `.output`, `.nuxt`, `.cache`
|
||||
- Static files: images, fonts, media, archives, and more
|
||||
|
||||
### Changed
|
||||
- Enhanced error logging with directory size report when content length is exceeded
|
||||
- Improved directory size calculation to exclude ignored files
|
||||
- Better directory size reporting with cleaner output format
|
||||
|
||||
## [1.1.4] - 2025-01-05
|
||||
|
||||
### Changed
|
||||
- Ignore files greater than 50KB during analysis
|
||||
|
||||
## [1.1.3] - 2025-01-05
|
||||
|
||||
### Changed
|
||||
- Enhanced error logging with detailed API request/response information
|
||||
- Added '[Hanzo]' prefix to all logs for better filtering
|
||||
- Improved error messages with more context and stack traces
|
||||
- Better handling of API response parsing errors
|
||||
|
||||
## [1.1.2] - 2025-01-04
|
||||
|
||||
### Added
|
||||
- Improved UI with better button states and transitions
|
||||
|
||||
### Changed
|
||||
- Improved error handling and user feedback
|
||||
- Enhanced file collection service with better logging
|
||||
|
||||
## [1.1.1] - 2025-01-02
|
||||
|
||||
### Changed
|
||||
- Improved file filtering to ignore media files during analysis
|
||||
- Add "refinement" to SPEC.md generation
|
||||
- Add "reanalyze" button
|
||||
|
||||
## [1.1.0] - 2025-01-02
|
||||
|
||||
### Added
|
||||
- Intelligent project analysis system
|
||||
- Automatic detection of project type (new vs existing)
|
||||
- Smart codebase scanning with selective file filtering
|
||||
- Comprehensive SPEC.md generation
|
||||
|
||||
- Improved UI/UX
|
||||
- Single unified "Analyze Project" action
|
||||
- Better loading states and error handling
|
||||
- Cleaner project details input
|
||||
|
||||
### Changed
|
||||
- Simplified project initialization flow
|
||||
- More descriptive user feedback messages
|
||||
- Better error handling and recovery
|
||||
|
||||
### Technical Improvements
|
||||
- Modular code architecture
|
||||
- Better type safety
|
||||
- Improved file filtering system
|
||||
- DRY code improvements
|
||||
|
||||
## [1.0.0] - 2024-12-30
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
- Project overview panel
|
||||
- Basic knowledge management features
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright 2025 Hanzo AI
|
||||
Copyright 2024 Hanzo AI
|
||||
|
||||
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,85 @@
|
||||
# Hanzo AI
|
||||
|
||||
Hanzo AI transforms your IDE into a full blown software engineering ninja.
|
||||
|
||||
Hanzo seamlessly manages context, tracks changes, and organizes knowledge across your codebase through advanced AI capabilities including vector search, symbolic search, and extended "thinking" processes.
|
||||
|
||||
## Features
|
||||
|
||||
- **Intelligent Context Management**: Automatically maintains project context across sessions
|
||||
- **Vector Search**: Find relevant code and documentation using semantic similarity
|
||||
- **Symbolic Search**: Discover code elements through structure and relationships
|
||||
- **Extended Thinking**: Leverage advanced reasoning for complex development tasks
|
||||
- **MCP Server Integration**: Connect to Meta Model Control Protocol servers for enhanced capabilities
|
||||
- **Automatic Documentation**: Generate comprehensive documentation from existing code
|
||||
- **Project Analysis**: Create detailed SPEC.md files through codebase analysis
|
||||
|
||||
## Usage
|
||||
|
||||
1. Open any project in VS Code
|
||||
2. Run `Hanzo: Open Project Manager` from the command palette (`Ctrl+Shift+P` / `Cmd+Shift+P`)
|
||||
3. The project dashboard will appear showing your project overview
|
||||
4. Enter additional project details if needed
|
||||
5. Click "Analyze Project" to generate specifications
|
||||
|
||||
The extension will:
|
||||
- Analyze your codebase structure and dependencies
|
||||
- Generate a comprehensive SPEC.md file
|
||||
- Create/update .cursorrules with project context
|
||||
- Build knowledge graphs for enhanced navigation
|
||||
|
||||
## Advanced Features
|
||||
|
||||
- **Knowledge Management**: Track changes and maintain history across your development lifecycle
|
||||
- **Rules-Based Assistance**: Define custom rules for code generation and recommendations
|
||||
- **Integration with LLMs**: Connect with various large language models for diverse capabilities
|
||||
|
||||
## Debugging
|
||||
|
||||
If you encounter "request too long" errors, create a `.hanzoignore` file in your project root using `.gitignore` format:
|
||||
|
||||
```
|
||||
staticfiles/
|
||||
media/
|
||||
large-binary.exe
|
||||
```
|
||||
|
||||
### Large Projects
|
||||
|
||||
Hanzo automatically chunks file data when processing:
|
||||
- Projects >10MB are split into multiple processing chunks
|
||||
- Each chunk is processed separately then combined
|
||||
- Progress displays in the VS Code notification area
|
||||
|
||||
For persistent size issues:
|
||||
1. Exclude more directories in `.hanzoignore`
|
||||
2. Remove large binaries or media files
|
||||
3. Focus analysis on core source directories
|
||||
|
||||
## Installation
|
||||
|
||||
Install from VS Code Marketplace:
|
||||
1. Open VS Code
|
||||
2. Press `Ctrl+P` / `Cmd+P`
|
||||
3. Type `ext install namanyayg.hanzo`
|
||||
|
||||
## Requirements
|
||||
|
||||
- VS Code 1.85.0+
|
||||
- Internet connection for AI capabilities
|
||||
|
||||
## Extension Settings
|
||||
|
||||
Hanzo works immediately without configuration. Access additional settings via:
|
||||
- VS Code settings (`Ctrl+,` / `Cmd+,`)
|
||||
- Hanzo configuration panel in the extension sidebar
|
||||
|
||||
## Known Issues & Limitations
|
||||
|
||||
- Large projects may require longer analysis time
|
||||
- Some file types excluded from analysis (binaries, media)
|
||||
- Performance may vary based on project complexity
|
||||
|
||||
## License
|
||||
|
||||
Licensed under MIT License. See LICENSE file for details.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Technical Specification
|
||||
|
||||
## System Overview
|
||||
The system is a Visual Studio Code (VS Code) extension designed to facilitate project analysis and management. It integrates with external APIs for authentication and data processing, and provides a user interface within VS Code for interaction. The main components include services for analysis, file collection, reminders, and status updates, as well as authentication management and API client functionalities.
|
||||
|
||||
## Core Functionality
|
||||
|
||||
### Authentication Management
|
||||
- **`AuthManager` (src/auth/manager.ts)**
|
||||
- **Singleton Pattern**: Ensures a single instance is used throughout the application.
|
||||
- **Key Methods**:
|
||||
- `generateRandomToken`: Generates a random token using `crypto.randomBytes`.
|
||||
- `getOrCreateClientId`: Retrieves or creates a client ID stored in VSCode's secrets.
|
||||
- `getStoredToken`, `storeToken`, `clearToken`: Manages the authentication token storage.
|
||||
- `isAuthenticated`, `getAuthToken`: Checks and retrieves the authentication token.
|
||||
- `startPolling`: Polls an external authentication service to check for a valid token.
|
||||
- `initiateAuth`: Initiates the authentication process, including generating auth state, opening an external browser for auth, and starting the polling process.
|
||||
- `logout`: Clears the stored token and stops any ongoing polling.
|
||||
|
||||
### API Client
|
||||
- **`ApiClient` (src/api/client.ts)**
|
||||
- **`makeAuthenticatedRequest`**: Handles making authenticated API requests.
|
||||
- **Token Retrieval**: Attempts to get an auth token from `AuthManager`.
|
||||
- **Body Stringification and Compression**: Stringifies the request body and compresses it if `shouldGzip` is `true`.
|
||||
- **Request Execution**: Makes the API request with appropriate headers and handles errors.
|
||||
- **Progress Handling**: Optionally shows a progress indicator during the request.
|
||||
|
||||
### Project Analysis
|
||||
- **`AnalysisService` (src/services/AnalysisService.ts)**
|
||||
- **`analyze(details?: string): Promise<void>`**: Core function that triggers the analysis process. It uses `ProjectManager` to handle the project operation and catches any errors to display them in the status bar and VS Code message window.
|
||||
- **Integration**: Singleton pattern, integration with `StatusBarService` for status updates, and error handling with detailed logging and user notifications.
|
||||
|
||||
### File Collection
|
||||
- **`FileCollectionService` (src/services/FileCollectionService.ts)**
|
||||
- **`collectFiles(): Promise<FileNode[]>`**: Main function to collect files from the workspace, respecting ignore patterns and size limits.
|
||||
- **`getDirectorySizeReport(): string`**: Generates a report of directory sizes.
|
||||
- **`getTotalSize(): number`**: Returns the total size of collected files.
|
||||
- **Integration**: Uses async generators for efficient directory scanning, integrates with `.gitignore` and `.hanzoignore` for custom ignore patterns, and provides detailed logging and summarization of included and ignored files.
|
||||
|
||||
### Reminder Service
|
||||
- **`ReminderService` (src/services/ReminderService.ts)**
|
||||
- **`triggerManually(): void`**: Manually triggers the reminder check.
|
||||
- **`dispose(): void`**: Cleans up resources and timers.
|
||||
- **Core Logic**: Periodic checks for significant changes in the workspace to prompt reanalysis. Uses both Git and file system tracking based on availability.
|
||||
- **Integration**: Debounced event handling, cooldown and forced reminder mechanisms, and integration with `StatusBarService` and `AnalysisService` for status updates and reanalysis.
|
||||
|
||||
### Status Bar Updates
|
||||
- **`StatusBarService` (src/services/StatusBarService.ts)**
|
||||
- **`setAnalyzing(message: string = 'Analyzing project...'): void`**
|
||||
- **`setIdle(): void`**
|
||||
- **`setError(message: string = 'Analysis failed'): void`**
|
||||
- **`setSuccess(message: string = 'Analysis complete'): void`**
|
||||
- **`dispose(): void`**
|
||||
- **Integration**: Singleton pattern to manage the VS Code status bar item, with different states for analyzing, idle, error, and success with appropriate tooltips and commands.
|
||||
|
||||
### Configuration Management
|
||||
- **`getConfig` (src/config.ts)**
|
||||
- **`getConfig(): Config`**: Retrieves the appropriate configuration based on the environment. It defaults to 'production' if the environment variable `VSCODE_ENV` is not set. This function is critical as it ensures the application uses the correct configuration for its operations, directly impacting its functionality and security.
|
||||
|
||||
### Extension Activation
|
||||
- **`activate` (src/extension.ts)**
|
||||
- **Description**: Initializes the extension, registers commands, and sets up the main functionality. It initializes the `AuthManager`, registers immediate commands, and initializes the extension API.
|
||||
- **Core Behavior**:
|
||||
- Registers commands like `hanzo.openManager`, `hanzo.login`, and others.
|
||||
- Initializes the `AuthManager` and stores the extension API for command access.
|
||||
- Calls `initializeExtension` to set up services and panel creation.
|
||||
|
||||
### Project Management
|
||||
- **`ProjectManager` (src/extension.ts)**
|
||||
- **Description**: Manages project-related operations, including file updates, UI updates, and project analysis.
|
||||
- **Core Methods**:
|
||||
- `updateProjectFiles`: Writes project specification and IDE-specific rules to files.
|
||||
- `updateUI`: Updates the webview panel UI based on the operation status.
|
||||
- `showNotification`: Displays notifications in VS Code.
|
||||
- `determineProjectType`: Determines whether to analyze or initialize a project.
|
||||
- `handleProjectOperation`: Handles the main project operation, including file collection, API calls, and specification refinement.
|
||||
|
||||
## Architecture
|
||||
The system is structured to facilitate seamless interaction between the VS Code extension and external APIs. Data flows from the user’s workspace through the `FileCollectionService`, which gathers and processes files. This data is then sent to the `AnalysisService` for project analysis, utilizing the `ApiClient` for authenticated API requests. The `AuthManager` handles all authentication-related operations, ensuring secure communication with external services. The `StatusBarService` provides real-time feedback to the user within VS Code, while the `ReminderService` ensures that users are prompted to reanalyze their projects when significant changes occur. The `ProjectManager` orchestrates the core project-related operations, integrating all services to provide a cohesive user experience.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
+29
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Sinon.JS
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# commons
|
||||
|
||||
[](https://circleci.com/gh/sinonjs/commons)
|
||||
[](https://codecov.io/gh/sinonjs/commons)
|
||||
<a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
|
||||
|
||||
Simple functions shared among the sinon end user libraries
|
||||
|
||||
## Rules
|
||||
|
||||
- Follows the [Sinon.JS compatibility](https://github.com/sinonjs/sinon/blob/master/CONTRIBUTING.md#compatibility)
|
||||
- 100% test coverage
|
||||
- Code formatted using [Prettier](https://prettier.io)
|
||||
- No side effects welcome! (only pure functions)
|
||||
- No platform specific functions
|
||||
- One export per file (any bundler can do tree shaking)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
var every = require("./prototypes/array").every;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function hasCallsLeft(callMap, spy) {
|
||||
if (callMap[spy.id] === undefined) {
|
||||
callMap[spy.id] = 0;
|
||||
}
|
||||
|
||||
return callMap[spy.id] < spy.callCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function checkAdjacentCalls(callMap, spy, index, spies) {
|
||||
var calledBeforeNext = true;
|
||||
|
||||
if (index !== spies.length - 1) {
|
||||
calledBeforeNext = spy.calledBefore(spies[index + 1]);
|
||||
}
|
||||
|
||||
if (hasCallsLeft(callMap, spy) && calledBeforeNext) {
|
||||
callMap[spy.id] += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Sinon proxy object (fake, spy, stub)
|
||||
* @typedef {object} SinonProxy
|
||||
* @property {Function} calledBefore - A method that determines if this proxy was called before another one
|
||||
* @property {string} id - Some id
|
||||
* @property {number} callCount - Number of times this proxy has been called
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns true when the spies have been called in the order they were supplied in
|
||||
* @param {SinonProxy[] | SinonProxy} spies An array of proxies, or several proxies as arguments
|
||||
* @returns {boolean} true when spies are called in order, false otherwise
|
||||
*/
|
||||
function calledInOrder(spies) {
|
||||
var callMap = {};
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
var _spies = arguments.length > 1 ? arguments : spies;
|
||||
|
||||
return every(_spies, checkAdjacentCalls.bind(null, callMap));
|
||||
}
|
||||
|
||||
module.exports = calledInOrder;
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var calledInOrder = require("./called-in-order");
|
||||
var sinon = require("@sinonjs/referee-sinon").sinon;
|
||||
|
||||
var testObject1 = {
|
||||
someFunction: function () {
|
||||
return;
|
||||
},
|
||||
};
|
||||
var testObject2 = {
|
||||
otherFunction: function () {
|
||||
return;
|
||||
},
|
||||
};
|
||||
var testObject3 = {
|
||||
thirdFunction: function () {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
function testMethod() {
|
||||
testObject1.someFunction();
|
||||
testObject2.otherFunction();
|
||||
testObject2.otherFunction();
|
||||
testObject2.otherFunction();
|
||||
testObject3.thirdFunction();
|
||||
}
|
||||
|
||||
describe("calledInOrder", function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(testObject1, "someFunction");
|
||||
sinon.stub(testObject2, "otherFunction");
|
||||
sinon.stub(testObject3, "thirdFunction");
|
||||
testMethod();
|
||||
});
|
||||
afterEach(function () {
|
||||
testObject1.someFunction.restore();
|
||||
testObject2.otherFunction.restore();
|
||||
testObject3.thirdFunction.restore();
|
||||
});
|
||||
|
||||
describe("given single array argument", function () {
|
||||
describe("when stubs were called in expected order", function () {
|
||||
it("returns true", function () {
|
||||
assert.isTrue(
|
||||
calledInOrder([
|
||||
testObject1.someFunction,
|
||||
testObject2.otherFunction,
|
||||
])
|
||||
);
|
||||
assert.isTrue(
|
||||
calledInOrder([
|
||||
testObject1.someFunction,
|
||||
testObject2.otherFunction,
|
||||
testObject2.otherFunction,
|
||||
testObject3.thirdFunction,
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when stubs were called in unexpected order", function () {
|
||||
it("returns false", function () {
|
||||
assert.isFalse(
|
||||
calledInOrder([
|
||||
testObject2.otherFunction,
|
||||
testObject1.someFunction,
|
||||
])
|
||||
);
|
||||
assert.isFalse(
|
||||
calledInOrder([
|
||||
testObject2.otherFunction,
|
||||
testObject1.someFunction,
|
||||
testObject1.someFunction,
|
||||
testObject3.thirdFunction,
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given multiple arguments", function () {
|
||||
describe("when stubs were called in expected order", function () {
|
||||
it("returns true", function () {
|
||||
assert.isTrue(
|
||||
calledInOrder(
|
||||
testObject1.someFunction,
|
||||
testObject2.otherFunction
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
calledInOrder(
|
||||
testObject1.someFunction,
|
||||
testObject2.otherFunction,
|
||||
testObject3.thirdFunction
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when stubs were called in unexpected order", function () {
|
||||
it("returns false", function () {
|
||||
assert.isFalse(
|
||||
calledInOrder(
|
||||
testObject2.otherFunction,
|
||||
testObject1.someFunction
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
calledInOrder(
|
||||
testObject2.otherFunction,
|
||||
testObject1.someFunction,
|
||||
testObject3.thirdFunction
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns a display name for a value from a constructor
|
||||
* @param {object} value A value to examine
|
||||
* @returns {(string|null)} A string or null
|
||||
*/
|
||||
function className(value) {
|
||||
const name = value.constructor && value.constructor.name;
|
||||
return name || null;
|
||||
}
|
||||
|
||||
module.exports = className;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
/* eslint-disable no-empty-function */
|
||||
|
||||
var assert = require("@sinonjs/referee").assert;
|
||||
var className = require("./class-name");
|
||||
|
||||
describe("className", function () {
|
||||
it("returns the class name of an instance", function () {
|
||||
// Because eslint-config-sinon disables es6, we can't
|
||||
// use a class definition here
|
||||
// https://github.com/sinonjs/eslint-config-sinon/blob/master/index.js
|
||||
// var instance = new (class TestClass {})();
|
||||
var instance = new (function TestClass() {})();
|
||||
var name = className(instance);
|
||||
assert.equals(name, "TestClass");
|
||||
});
|
||||
|
||||
it("returns 'Object' for {}", function () {
|
||||
var name = className({});
|
||||
assert.equals(name, "Object");
|
||||
});
|
||||
|
||||
it("returns null for an object that has no prototype", function () {
|
||||
var obj = Object.create(null);
|
||||
var name = className(obj);
|
||||
assert.equals(name, null);
|
||||
});
|
||||
|
||||
it("returns null for an object whose prototype was mangled", function () {
|
||||
// This is what Node v6 and v7 do for objects returned by querystring.parse()
|
||||
function MangledObject() {}
|
||||
MangledObject.prototype = Object.create(null);
|
||||
var obj = new MangledObject();
|
||||
var name = className(obj);
|
||||
assert.equals(name, null);
|
||||
});
|
||||
});
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable no-console */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns a function that will invoke the supplied function and print a
|
||||
* deprecation warning to the console each time it is called.
|
||||
* @param {Function} func
|
||||
* @param {string} msg
|
||||
* @returns {Function}
|
||||
*/
|
||||
exports.wrap = function (func, msg) {
|
||||
var wrapped = function () {
|
||||
exports.printWarning(msg);
|
||||
return func.apply(this, arguments);
|
||||
};
|
||||
if (func.prototype) {
|
||||
wrapped.prototype = func.prototype;
|
||||
}
|
||||
return wrapped;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a string which can be supplied to `wrap()` to notify the user that a
|
||||
* particular part of the sinon API has been deprecated.
|
||||
* @param {string} packageName
|
||||
* @param {string} funcName
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.defaultMsg = function (packageName, funcName) {
|
||||
return `${packageName}.${funcName} is deprecated and will be removed from the public API in a future version of ${packageName}.`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints a warning on the console, when it exists
|
||||
* @param {string} msg
|
||||
* @returns {undefined}
|
||||
*/
|
||||
exports.printWarning = function (msg) {
|
||||
/* istanbul ignore next */
|
||||
if (typeof process === "object" && process.emitWarning) {
|
||||
// Emit Warnings in Node
|
||||
process.emitWarning(msg);
|
||||
} else if (console.info) {
|
||||
console.info(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
};
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/* eslint-disable no-console */
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var sinon = require("@sinonjs/referee-sinon").sinon;
|
||||
|
||||
var deprecated = require("./deprecated");
|
||||
|
||||
var msg = "test";
|
||||
|
||||
describe("deprecated", function () {
|
||||
describe("defaultMsg", function () {
|
||||
it("should return a string", function () {
|
||||
assert.equals(
|
||||
deprecated.defaultMsg("sinon", "someFunc"),
|
||||
"sinon.someFunc is deprecated and will be removed from the public API in a future version of sinon."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("printWarning", function () {
|
||||
beforeEach(function () {
|
||||
sinon.replace(process, "emitWarning", sinon.fake());
|
||||
});
|
||||
|
||||
afterEach(sinon.restore);
|
||||
|
||||
describe("when `process.emitWarning` is defined", function () {
|
||||
it("should call process.emitWarning with a msg", function () {
|
||||
deprecated.printWarning(msg);
|
||||
assert.calledOnceWith(process.emitWarning, msg);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when `process.emitWarning` is undefined", function () {
|
||||
beforeEach(function () {
|
||||
sinon.replace(console, "info", sinon.fake());
|
||||
sinon.replace(console, "log", sinon.fake());
|
||||
process.emitWarning = undefined;
|
||||
});
|
||||
|
||||
afterEach(sinon.restore);
|
||||
|
||||
describe("when `console.info` is defined", function () {
|
||||
it("should call `console.info` with a message", function () {
|
||||
deprecated.printWarning(msg);
|
||||
assert.calledOnceWith(console.info, msg);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when `console.info` is undefined", function () {
|
||||
it("should call `console.log` with a message", function () {
|
||||
console.info = undefined;
|
||||
deprecated.printWarning(msg);
|
||||
assert.calledOnceWith(console.log, msg);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("wrap", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
var method = sinon.fake();
|
||||
var wrapped;
|
||||
|
||||
beforeEach(function () {
|
||||
wrapped = deprecated.wrap(method, msg);
|
||||
});
|
||||
|
||||
it("should return a wrapper function", function () {
|
||||
assert.match(wrapped, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should assign the prototype of the passed method", function () {
|
||||
assert.equals(method.prototype, wrapped.prototype);
|
||||
});
|
||||
|
||||
context("when the passed method has falsy prototype", function () {
|
||||
it("should not be assigned to the wrapped method", function () {
|
||||
method.prototype = null;
|
||||
wrapped = deprecated.wrap(method, msg);
|
||||
assert.match(wrapped.prototype, sinon.match.object);
|
||||
});
|
||||
});
|
||||
|
||||
context("when invoking the wrapped function", function () {
|
||||
before(function () {
|
||||
sinon.replace(deprecated, "printWarning", sinon.fake());
|
||||
wrapped({});
|
||||
});
|
||||
|
||||
it("should call `printWarning` before invoking", function () {
|
||||
assert.calledOnceWith(deprecated.printWarning, msg);
|
||||
});
|
||||
|
||||
it("should invoke the passed method with the given arguments", function () {
|
||||
assert.calledOnceWith(method, {});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns true when fn returns true for all members of obj.
|
||||
* This is an every implementation that works for all iterables
|
||||
* @param {object} obj
|
||||
* @param {Function} fn
|
||||
* @returns {boolean}
|
||||
*/
|
||||
module.exports = function every(obj, fn) {
|
||||
var pass = true;
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
|
||||
obj.forEach(function () {
|
||||
if (!fn.apply(this, arguments)) {
|
||||
// Throwing an error is the only way to break `forEach`
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
pass = false;
|
||||
}
|
||||
|
||||
return pass;
|
||||
};
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var sinon = require("@sinonjs/referee-sinon").sinon;
|
||||
var every = require("./every");
|
||||
|
||||
describe("util/core/every", function () {
|
||||
it("returns true when the callback function returns true for every element in an iterable", function () {
|
||||
var obj = [true, true, true, true];
|
||||
var allTrue = every(obj, function (val) {
|
||||
return val;
|
||||
});
|
||||
|
||||
assert(allTrue);
|
||||
});
|
||||
|
||||
it("returns false when the callback function returns false for any element in an iterable", function () {
|
||||
var obj = [true, true, true, false];
|
||||
var result = every(obj, function (val) {
|
||||
return val;
|
||||
});
|
||||
|
||||
assert.isFalse(result);
|
||||
});
|
||||
|
||||
it("calls the given callback once for each item in an iterable until it returns false", function () {
|
||||
var iterableOne = [true, true, true, true];
|
||||
var iterableTwo = [true, true, false, true];
|
||||
var callback = sinon.spy(function (val) {
|
||||
return val;
|
||||
});
|
||||
|
||||
every(iterableOne, callback);
|
||||
assert.equals(callback.callCount, 4);
|
||||
|
||||
callback.resetHistory();
|
||||
|
||||
every(iterableTwo, callback);
|
||||
assert.equals(callback.callCount, 3);
|
||||
});
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns a display name for a function
|
||||
* @param {Function} func
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports = function functionName(func) {
|
||||
if (!func) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return (
|
||||
func.displayName ||
|
||||
func.name ||
|
||||
// Use function decomposition as a last resort to get function
|
||||
// name. Does not rely on function decomposition to work - if it
|
||||
// doesn't debugging will be slightly less informative
|
||||
// (i.e. toString will say 'spy' rather than 'myFunc').
|
||||
(String(func).match(/function ([^\s(]+)/) || [])[1]
|
||||
);
|
||||
} catch (e) {
|
||||
// Stringify may fail and we might get an exception, as a last-last
|
||||
// resort fall back to empty string.
|
||||
return "";
|
||||
}
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
|
||||
var jsc = require("jsverify");
|
||||
var refute = require("@sinonjs/referee-sinon").refute;
|
||||
|
||||
var functionName = require("./function-name");
|
||||
|
||||
describe("function-name", function () {
|
||||
it("should return empty string if func is falsy", function () {
|
||||
jsc.assertForall("falsy", function (fn) {
|
||||
return functionName(fn) === "";
|
||||
});
|
||||
});
|
||||
|
||||
it("should use displayName by default", function () {
|
||||
jsc.assertForall("nestring", function (displayName) {
|
||||
var fn = { displayName: displayName };
|
||||
|
||||
return functionName(fn) === fn.displayName;
|
||||
});
|
||||
});
|
||||
|
||||
it("should use name if displayName is not available", function () {
|
||||
jsc.assertForall("nestring", function (name) {
|
||||
var fn = { name: name };
|
||||
|
||||
return functionName(fn) === fn.name;
|
||||
});
|
||||
});
|
||||
|
||||
it("should fallback to string parsing", function () {
|
||||
jsc.assertForall("nat", function (naturalNumber) {
|
||||
var name = `fn${naturalNumber}`;
|
||||
var fn = {
|
||||
toString: function () {
|
||||
return `\nfunction ${name}`;
|
||||
},
|
||||
};
|
||||
|
||||
return functionName(fn) === name;
|
||||
});
|
||||
});
|
||||
|
||||
it("should not fail when a name cannot be found", function () {
|
||||
refute.exception(function () {
|
||||
var fn = {
|
||||
toString: function () {
|
||||
return "\nfunction (";
|
||||
},
|
||||
};
|
||||
|
||||
functionName(fn);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not fail when toString is undefined", function () {
|
||||
refute.exception(function () {
|
||||
functionName(Object.create(null));
|
||||
});
|
||||
});
|
||||
|
||||
it("should not fail when toString throws", function () {
|
||||
refute.exception(function () {
|
||||
var fn;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
fn = eval("(function*() {})")().constructor;
|
||||
} catch (e) {
|
||||
// env doesn't support generators
|
||||
return;
|
||||
}
|
||||
|
||||
functionName(fn);
|
||||
});
|
||||
});
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A reference to the global object
|
||||
* @type {object} globalObject
|
||||
*/
|
||||
var globalObject;
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (typeof global !== "undefined") {
|
||||
// Node
|
||||
globalObject = global;
|
||||
} else if (typeof window !== "undefined") {
|
||||
// Browser
|
||||
globalObject = window;
|
||||
} else {
|
||||
// WebWorker
|
||||
globalObject = self;
|
||||
}
|
||||
|
||||
module.exports = globalObject;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var globalObject = require("./global");
|
||||
|
||||
describe("global", function () {
|
||||
before(function () {
|
||||
if (typeof global === "undefined") {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("is same as global", function () {
|
||||
assert.same(globalObject, global);
|
||||
});
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
global: require("./global"),
|
||||
calledInOrder: require("./called-in-order"),
|
||||
className: require("./class-name"),
|
||||
deprecated: require("./deprecated"),
|
||||
every: require("./every"),
|
||||
functionName: require("./function-name"),
|
||||
orderByFirstCall: require("./order-by-first-call"),
|
||||
prototypes: require("./prototypes"),
|
||||
typeOf: require("./type-of"),
|
||||
valueToString: require("./value-to-string"),
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var index = require("./index");
|
||||
|
||||
var expectedMethods = [
|
||||
"calledInOrder",
|
||||
"className",
|
||||
"every",
|
||||
"functionName",
|
||||
"orderByFirstCall",
|
||||
"typeOf",
|
||||
"valueToString",
|
||||
];
|
||||
var expectedObjectProperties = ["deprecated", "prototypes"];
|
||||
|
||||
describe("package", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
expectedMethods.forEach(function (name) {
|
||||
it(`should export a method named ${name}`, function () {
|
||||
assert.isFunction(index[name]);
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
expectedObjectProperties.forEach(function (name) {
|
||||
it(`should export an object property named ${name}`, function () {
|
||||
assert.isObject(index[name]);
|
||||
});
|
||||
});
|
||||
});
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
var sort = require("./prototypes/array").sort;
|
||||
var slice = require("./prototypes/array").slice;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function comparator(a, b) {
|
||||
// uuid, won't ever be equal
|
||||
var aCall = a.getCall(0);
|
||||
var bCall = b.getCall(0);
|
||||
var aId = (aCall && aCall.callId) || -1;
|
||||
var bId = (bCall && bCall.callId) || -1;
|
||||
|
||||
return aId < bId ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Sinon proxy object (fake, spy, stub)
|
||||
* @typedef {object} SinonProxy
|
||||
* @property {Function} getCall - A method that can return the first call
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sorts an array of SinonProxy instances (fake, spy, stub) by their first call
|
||||
* @param {SinonProxy[] | SinonProxy} spies
|
||||
* @returns {SinonProxy[]}
|
||||
*/
|
||||
function orderByFirstCall(spies) {
|
||||
return sort(slice(spies), comparator);
|
||||
}
|
||||
|
||||
module.exports = orderByFirstCall;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var knuthShuffle = require("knuth-shuffle").knuthShuffle;
|
||||
var sinon = require("@sinonjs/referee-sinon").sinon;
|
||||
var orderByFirstCall = require("./order-by-first-call");
|
||||
|
||||
describe("orderByFirstCall", function () {
|
||||
it("should order an Array of spies by the callId of the first call, ascending", function () {
|
||||
// create an array of spies
|
||||
var spies = [
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
sinon.spy(),
|
||||
];
|
||||
|
||||
// call all the spies
|
||||
spies.forEach(function (spy) {
|
||||
spy();
|
||||
});
|
||||
|
||||
// add a few uncalled spies
|
||||
spies.push(sinon.spy());
|
||||
spies.push(sinon.spy());
|
||||
|
||||
// randomise the order of the spies
|
||||
knuthShuffle(spies);
|
||||
|
||||
var sortedSpies = orderByFirstCall(spies);
|
||||
|
||||
assert.equals(sortedSpies.length, spies.length);
|
||||
|
||||
var orderedByFirstCall = sortedSpies.every(function (spy, index) {
|
||||
if (index + 1 === sortedSpies.length) {
|
||||
return true;
|
||||
}
|
||||
var nextSpy = sortedSpies[index + 1];
|
||||
|
||||
// uncalled spies should be ordered first
|
||||
if (!spy.called) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spy.calledImmediatelyBefore(nextSpy);
|
||||
});
|
||||
|
||||
assert.isTrue(orderedByFirstCall);
|
||||
});
|
||||
});
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Prototypes
|
||||
|
||||
The functions in this folder are to be use for keeping cached references to the built-in prototypes, so that people can't inadvertently break the library by making mistakes in userland.
|
||||
|
||||
See https://github.com/sinonjs/sinon/pull/1523
|
||||
|
||||
## Without cached references
|
||||
|
||||
```js
|
||||
// in userland, the library user needs to replace the filter method on
|
||||
// Array.prototype
|
||||
var array = [1, 2, 3];
|
||||
sinon.replace(array, "filter", sinon.fake.returns(2));
|
||||
|
||||
// in a sinon module, the library author needs to use the filter method
|
||||
var someArray = ["a", "b", 42, "c"];
|
||||
var answer = filter(someArray, function (v) {
|
||||
return v === 42;
|
||||
});
|
||||
|
||||
console.log(answer);
|
||||
// => 2
|
||||
```
|
||||
|
||||
## With cached references
|
||||
|
||||
```js
|
||||
// in userland, the library user needs to replace the filter method on
|
||||
// Array.prototype
|
||||
var array = [1, 2, 3];
|
||||
sinon.replace(array, "filter", sinon.fake.returns(2));
|
||||
|
||||
// in a sinon module, the library author needs to use the filter method
|
||||
// get a reference to the original Array.prototype.filter
|
||||
var filter = require("@sinonjs/commons").prototypes.array.filter;
|
||||
var someArray = ["a", "b", 42, "c"];
|
||||
var answer = filter(someArray, function (v) {
|
||||
return v === 42;
|
||||
});
|
||||
|
||||
console.log(answer);
|
||||
// => 42
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(Array.prototype);
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
|
||||
var call = Function.call;
|
||||
var throwsOnProto = require("./throws-on-proto");
|
||||
|
||||
var disallowedProperties = [
|
||||
// ignore size because it throws from Map
|
||||
"size",
|
||||
"caller",
|
||||
"callee",
|
||||
"arguments",
|
||||
];
|
||||
|
||||
// This branch is covered when tests are run with `--disable-proto=throw`,
|
||||
// however we can test both branches at the same time, so this is ignored
|
||||
/* istanbul ignore next */
|
||||
if (throwsOnProto) {
|
||||
disallowedProperties.push("__proto__");
|
||||
}
|
||||
|
||||
module.exports = function copyPrototypeMethods(prototype) {
|
||||
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
|
||||
return Object.getOwnPropertyNames(prototype).reduce(function (
|
||||
result,
|
||||
name
|
||||
) {
|
||||
if (disallowedProperties.includes(name)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof prototype[name] !== "function") {
|
||||
return result;
|
||||
}
|
||||
|
||||
result[name] = call.bind(prototype[name]);
|
||||
|
||||
return result;
|
||||
},
|
||||
Object.create(null));
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
var refute = require("@sinonjs/referee-sinon").refute;
|
||||
var copyPrototypeMethods = require("./copy-prototype-methods");
|
||||
|
||||
describe("copyPrototypeMethods", function () {
|
||||
it("does not throw for Map", function () {
|
||||
refute.exception(function () {
|
||||
copyPrototypeMethods(Map.prototype);
|
||||
});
|
||||
});
|
||||
});
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(Function.prototype);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
array: require("./array"),
|
||||
function: require("./function"),
|
||||
map: require("./map"),
|
||||
object: require("./object"),
|
||||
set: require("./set"),
|
||||
string: require("./string"),
|
||||
};
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
|
||||
var arrayProto = require("./index").array;
|
||||
var functionProto = require("./index").function;
|
||||
var mapProto = require("./index").map;
|
||||
var objectProto = require("./index").object;
|
||||
var setProto = require("./index").set;
|
||||
var stringProto = require("./index").string;
|
||||
var throwsOnProto = require("./throws-on-proto");
|
||||
|
||||
describe("prototypes", function () {
|
||||
describe(".array", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(arrayProto, Array);
|
||||
});
|
||||
describe(".function", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(functionProto, Function);
|
||||
});
|
||||
describe(".map", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(mapProto, Map);
|
||||
});
|
||||
describe(".object", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(objectProto, Object);
|
||||
});
|
||||
describe(".set", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(setProto, Set);
|
||||
});
|
||||
describe(".string", function () {
|
||||
// eslint-disable-next-line mocha/no-setup-in-describe
|
||||
verifyProperties(stringProto, String);
|
||||
});
|
||||
});
|
||||
|
||||
function verifyProperties(p, origin) {
|
||||
var disallowedProperties = ["size", "caller", "callee", "arguments"];
|
||||
if (throwsOnProto) {
|
||||
disallowedProperties.push("__proto__");
|
||||
}
|
||||
|
||||
it("should have all the methods of the origin prototype", function () {
|
||||
var methodNames = Object.getOwnPropertyNames(origin.prototype).filter(
|
||||
function (name) {
|
||||
if (disallowedProperties.includes(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof origin.prototype[name] === "function";
|
||||
}
|
||||
);
|
||||
|
||||
methodNames.forEach(function (name) {
|
||||
assert.isTrue(Object.prototype.hasOwnProperty.call(p, name), name);
|
||||
});
|
||||
});
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(Map.prototype);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(Object.prototype);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(Set.prototype);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var copyPrototype = require("./copy-prototype-methods");
|
||||
|
||||
module.exports = copyPrototype(String.prototype);
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Is true when the environment causes an error to be thrown for accessing the
|
||||
* __proto__ property.
|
||||
* This is necessary in order to support `node --disable-proto=throw`.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
|
||||
* @type {boolean}
|
||||
*/
|
||||
let throwsOnProto;
|
||||
try {
|
||||
const object = {};
|
||||
// eslint-disable-next-line no-proto, no-unused-expressions
|
||||
object.__proto__;
|
||||
throwsOnProto = false;
|
||||
} catch (_) {
|
||||
// This branch is covered when tests are run with `--disable-proto=throw`,
|
||||
// however we can test both branches at the same time, so this is ignored
|
||||
/* istanbul ignore next */
|
||||
throwsOnProto = true;
|
||||
}
|
||||
|
||||
module.exports = throwsOnProto;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
var type = require("type-detect");
|
||||
|
||||
/**
|
||||
* Returns the lower-case result of running type from type-detect on the value
|
||||
* @param {*} value
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports = function typeOf(value) {
|
||||
return type(value).toLowerCase();
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var typeOf = require("./type-of");
|
||||
|
||||
describe("typeOf", function () {
|
||||
it("returns boolean", function () {
|
||||
assert.equals(typeOf(false), "boolean");
|
||||
});
|
||||
|
||||
it("returns string", function () {
|
||||
assert.equals(typeOf("Sinon.JS"), "string");
|
||||
});
|
||||
|
||||
it("returns number", function () {
|
||||
assert.equals(typeOf(123), "number");
|
||||
});
|
||||
|
||||
it("returns object", function () {
|
||||
assert.equals(typeOf({}), "object");
|
||||
});
|
||||
|
||||
it("returns function", function () {
|
||||
assert.equals(
|
||||
typeOf(function () {
|
||||
return undefined;
|
||||
}),
|
||||
"function"
|
||||
);
|
||||
});
|
||||
|
||||
it("returns undefined", function () {
|
||||
assert.equals(typeOf(undefined), "undefined");
|
||||
});
|
||||
|
||||
it("returns null", function () {
|
||||
assert.equals(typeOf(null), "null");
|
||||
});
|
||||
|
||||
it("returns array", function () {
|
||||
assert.equals(typeOf([]), "array");
|
||||
});
|
||||
|
||||
it("returns regexp", function () {
|
||||
assert.equals(typeOf(/.*/), "regexp");
|
||||
});
|
||||
|
||||
it("returns date", function () {
|
||||
assert.equals(typeOf(new Date()), "date");
|
||||
});
|
||||
});
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns a string representation of the value
|
||||
* @param {*} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function valueToString(value) {
|
||||
if (value && value.toString) {
|
||||
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
|
||||
return value.toString();
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
module.exports = valueToString;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
var assert = require("@sinonjs/referee-sinon").assert;
|
||||
var valueToString = require("./value-to-string");
|
||||
|
||||
describe("util/core/valueToString", function () {
|
||||
it("returns string representation of an object", function () {
|
||||
var obj = {};
|
||||
|
||||
assert.equals(valueToString(obj), obj.toString());
|
||||
});
|
||||
|
||||
it("returns 'null' for literal null'", function () {
|
||||
assert.equals(valueToString(null), "null");
|
||||
});
|
||||
|
||||
it("returns 'undefined' for literal undefined", function () {
|
||||
assert.equals(valueToString(undefined), "undefined");
|
||||
});
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "@sinonjs/commons",
|
||||
"version": "3.0.1",
|
||||
"description": "Simple functions shared among the sinon end user libraries",
|
||||
"main": "lib/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rm -rf types && tsc",
|
||||
"lint": "eslint .",
|
||||
"precommit": "lint-staged",
|
||||
"test": "mocha --recursive -R dot \"lib/**/*.test.js\"",
|
||||
"test-check-coverage": "npm run test-coverage && nyc check-coverage --branches 100 --functions 100 --lines 100",
|
||||
"test-coverage": "nyc --reporter text --reporter html --reporter lcovonly npm run test",
|
||||
"prepublishOnly": "npm run build",
|
||||
"prettier:check": "prettier --check '**/*.{js,css,md}'",
|
||||
"prettier:write": "prettier --write '**/*.{js,css,md}'",
|
||||
"preversion": "npm run test-check-coverage",
|
||||
"version": "changes --commits --footer",
|
||||
"postversion": "git push --follow-tags && npm publish",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sinonjs/commons.git"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"types"
|
||||
],
|
||||
"author": "",
|
||||
"license": "BSD-3-Clause",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sinonjs/commons/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sinonjs/commons#readme",
|
||||
"lint-staged": {
|
||||
"*.{js,css,md}": "prettier --check",
|
||||
"*.js": "eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sinonjs/eslint-config": "^4.0.6",
|
||||
"@sinonjs/eslint-plugin-no-prototype-methods": "^0.1.0",
|
||||
"@sinonjs/referee-sinon": "^10.1.0",
|
||||
"@studio/changes": "^2.2.0",
|
||||
"husky": "^6.0.0",
|
||||
"jsverify": "0.8.4",
|
||||
"knuth-shuffle": "^1.0.8",
|
||||
"lint-staged": "^13.0.3",
|
||||
"mocha": "^10.1.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
# `@sinonjs/fake-timers`
|
||||
|
||||
[](https://codecov.io/gh/sinonjs/fake-timers)
|
||||
<a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
|
||||
|
||||
JavaScript implementation of the timer APIs; `setTimeout`, `clearTimeout`, `setImmediate`, `clearImmediate`, `setInterval`, `clearInterval`, `requestAnimationFrame`, `cancelAnimationFrame`, `requestIdleCallback`, and `cancelIdleCallback`, along with a clock instance that controls the flow of time. FakeTimers also provides a `Date` implementation that gets its time from the clock.
|
||||
|
||||
In addition in browser environment `@sinonjs/fake-timers` provides a `performance` implementation that gets its time from the clock. In Node environments FakeTimers provides a `nextTick` implementation that is synchronized with the clock - and a `process.hrtime` shim that works with the clock.
|
||||
|
||||
`@sinonjs/fake-timers` can be used to simulate passing time in automated tests and other
|
||||
situations where you want the scheduling semantics, but don't want to actually
|
||||
wait.
|
||||
|
||||
`@sinonjs/fake-timers` is extracted from [Sinon.JS](https://github.com/sinonjs/sinon.js) and targets the [same runtimes](https://sinonjs.org/releases/latest/#supported-runtimes).
|
||||
|
||||
## Autocomplete, IntelliSense and TypeScript definitions
|
||||
|
||||
Version 7 introduced JSDoc to the codebase. This should provide autocomplete and type suggestions in supporting IDEs. If you need more elaborate type support, TypeScript definitions for the Sinon projects are independently maintained by the Definitely Types community:
|
||||
|
||||
```
|
||||
npm install -D @types/sinonjs__fake-timers
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
`@sinonjs/fake-timers` can be used in both Node and browser environments. Installation is as easy as
|
||||
|
||||
```sh
|
||||
npm install @sinonjs/fake-timers
|
||||
```
|
||||
|
||||
If you want to use `@sinonjs/fake-timers` in a browser you can either build your own bundle or use [Skypack](https://www.skypack.dev).
|
||||
|
||||
## Usage
|
||||
|
||||
To use `@sinonjs/fake-timers`, create a new clock, schedule events on it using the timer
|
||||
functions and pass time using the `tick` method.
|
||||
|
||||
```js
|
||||
// In the browser distribution, a global `FakeTimers` is already available
|
||||
var FakeTimers = require("@sinonjs/fake-timers");
|
||||
var clock = FakeTimers.createClock();
|
||||
|
||||
clock.setTimeout(function () {
|
||||
console.log(
|
||||
"The poblano is a mild chili pepper originating in the state of Puebla, Mexico.",
|
||||
);
|
||||
}, 15);
|
||||
|
||||
// ...
|
||||
|
||||
clock.tick(15);
|
||||
```
|
||||
|
||||
Upon executing the last line, an interesting fact about the
|
||||
[Poblano](https://en.wikipedia.org/wiki/Poblano) will be printed synchronously to
|
||||
the screen. If you want to simulate asynchronous behavior, please see the `async` function variants (eg `clock.tick(time)` vs `await clock.tickAsync(time)`).
|
||||
|
||||
The `next`, `runAll`, `runToFrame`, and `runToLast` methods are available to advance the clock. See the
|
||||
API Reference for more details.
|
||||
|
||||
### Faking the native timers
|
||||
|
||||
When using `@sinonjs/fake-timers` to test timers, you will most likely want to replace the native
|
||||
timers such that calling `setTimeout` actually schedules a callback with your
|
||||
clock instance, not the browser's internals.
|
||||
|
||||
Calling `install` with no arguments achieves this. You can call `uninstall`
|
||||
later to restore things as they were again.
|
||||
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html) and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when using global scope.
|
||||
|
||||
```js
|
||||
// In the browser distribution, a global `FakeTimers` is already available
|
||||
var FakeTimers = require("@sinonjs/fake-timers");
|
||||
|
||||
var clock = FakeTimers.install();
|
||||
// Equivalent to
|
||||
// var clock = FakeTimers.install(typeof global !== "undefined" ? global : window);
|
||||
|
||||
setTimeout(fn, 15); // Schedules with clock.setTimeout
|
||||
|
||||
clock.uninstall();
|
||||
// setTimeout is restored to the native implementation
|
||||
```
|
||||
|
||||
To hijack timers in another context pass it to the `install` method.
|
||||
|
||||
```js
|
||||
var FakeTimers = require("@sinonjs/fake-timers");
|
||||
var context = {
|
||||
setTimeout: setTimeout, // By default context.setTimeout uses the global setTimeout
|
||||
};
|
||||
var clock = FakeTimers.withGlobal(context).install();
|
||||
|
||||
context.setTimeout(fn, 15); // Schedules with clock.setTimeout
|
||||
|
||||
clock.uninstall();
|
||||
// context.setTimeout is restored to the original implementation
|
||||
```
|
||||
|
||||
Usually you want to install the timers onto the global object, so call `install`
|
||||
without arguments.
|
||||
|
||||
#### Automatically incrementing mocked time
|
||||
|
||||
FakeTimers supports the possibility to attach the faked timers to any change
|
||||
in the real system time. This means that there is no need to `tick()` the
|
||||
clock in a situation where you won't know **when** to call `tick()`.
|
||||
|
||||
Please note that this is achieved using the original setImmediate() API at a certain
|
||||
configurable interval `config.advanceTimeDelta` (default: 20ms). Meaning time would
|
||||
be incremented every 20ms, not in real time.
|
||||
|
||||
An example would be:
|
||||
|
||||
```js
|
||||
var FakeTimers = require("@sinonjs/fake-timers");
|
||||
var clock = FakeTimers.install({
|
||||
shouldAdvanceTime: true,
|
||||
advanceTimeDelta: 40,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("this just timed out"); //executed after 40ms
|
||||
}, 30);
|
||||
|
||||
setImmediate(() => {
|
||||
console.log("not so immediate"); //executed after 40ms
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("this timed out after"); //executed after 80ms
|
||||
clock.uninstall();
|
||||
}, 50);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `var clock = FakeTimers.createClock([now[, loopLimit]])`
|
||||
|
||||
Creates a clock. The default
|
||||
[epoch](https://en.wikipedia.org/wiki/Epoch_%28reference_date%29) is `0`.
|
||||
|
||||
The `now` argument may be a number (in milliseconds) or a Date object.
|
||||
|
||||
The `loopLimit` argument sets the maximum number of timers that will be run when calling `runAll()` before assuming that we have an infinite loop and throwing an error. The default is `1000`.
|
||||
|
||||
### `var clock = FakeTimers.install([config])`
|
||||
|
||||
Installs FakeTimers using the specified config (otherwise with epoch `0` on the global scope).
|
||||
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html) and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when using global scope.
|
||||
The following configuration options are available
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `config.now` | Number/Date | 0 | installs FakeTimers with the specified unix epoch |
|
||||
| `config.toFake` | String[] | ["setTimeout", "clearTimeout", "setImmediate", "clearImmediate","setInterval", "clearInterval", "Date", "requestAnimationFrame", "cancelAnimationFrame", "requestIdleCallback", "cancelIdleCallback", "hrtime", "performance"] | an array with explicit function names (or objects, in the case of "performance") to hijack. _When not set, FakeTimers will automatically fake all methods **except** `nextTick`_ e.g., `FakeTimers.install({ toFake: ["setTimeout","nextTick"]})` will fake only `setTimeout` and `nextTick` |
|
||||
| `config.loopLimit` | Number | 1000 | the maximum number of timers that will be run when calling runAll() |
|
||||
| `config.shouldAdvanceTime` | Boolean | false | tells FakeTimers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time) |
|
||||
| `config.advanceTimeDelta` | Number | 20 | relevant only when using with `shouldAdvanceTime: true`. increment mocked time by `advanceTimeDelta` ms every `advanceTimeDelta` ms change in the real system time. |
|
||||
| `config.shouldClearNativeTimers` | Boolean | false | tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers. |
|
||||
|
||||
### `var id = clock.setTimeout(callback, timeout)`
|
||||
|
||||
Schedules the callback to be fired once `timeout` milliseconds have ticked by.
|
||||
|
||||
In Node.js `setTimeout` returns a timer object. FakeTimers will do the same, however
|
||||
its `ref()` and `unref()` methods have no effect.
|
||||
|
||||
In browsers a timer ID is returned.
|
||||
|
||||
### `clock.clearTimeout(id)`
|
||||
|
||||
Clears the timer given the ID or timer object, as long as it was created using
|
||||
`setTimeout`.
|
||||
|
||||
### `var id = clock.setInterval(callback, timeout)`
|
||||
|
||||
Schedules the callback to be fired every time `timeout` milliseconds have ticked
|
||||
by.
|
||||
|
||||
In Node.js `setInterval` returns a timer object. FakeTimers will do the same, however
|
||||
its `ref()` and `unref()` methods have no effect.
|
||||
|
||||
In browsers a timer ID is returned.
|
||||
|
||||
### `clock.clearInterval(id)`
|
||||
|
||||
Clears the timer given the ID or timer object, as long as it was created using
|
||||
`setInterval`.
|
||||
|
||||
### `var id = clock.setImmediate(callback)`
|
||||
|
||||
Schedules the callback to be fired once `0` milliseconds have ticked by. Note
|
||||
that you'll still have to call `clock.tick()` for the callback to fire. If
|
||||
called during a tick the callback won't fire until `1` millisecond has ticked
|
||||
by.
|
||||
|
||||
In Node.js `setImmediate` returns a timer object. FakeTimers will do the same,
|
||||
however its `ref()` and `unref()` methods have no effect.
|
||||
|
||||
In browsers a timer ID is returned.
|
||||
|
||||
### `clock.clearImmediate(id)`
|
||||
|
||||
Clears the timer given the ID or timer object, as long as it was created using
|
||||
`setImmediate`.
|
||||
|
||||
### `clock.requestAnimationFrame(callback)`
|
||||
|
||||
Schedules the callback to be fired on the next animation frame, which runs every
|
||||
16 ticks. Returns an `id` which can be used to cancel the callback. This is
|
||||
available in both browser & node environments.
|
||||
|
||||
### `clock.cancelAnimationFrame(id)`
|
||||
|
||||
Cancels the callback scheduled by the provided id.
|
||||
|
||||
### `clock.requestIdleCallback(callback[, timeout])`
|
||||
|
||||
Queued the callback to be fired during idle periods to perform background and low priority work on the main event loop. Callbacks which have a timeout option will be fired no later than time in milliseconds. Returns an `id` which can be used to cancel the callback.
|
||||
|
||||
### `clock.cancelIdleCallback(id)`
|
||||
|
||||
Cancels the callback scheduled by the provided id.
|
||||
|
||||
### `clock.countTimers()`
|
||||
|
||||
Returns the number of waiting timers. This can be used to assert that a test
|
||||
finishes without leaking any timers.
|
||||
|
||||
### `clock.hrtime(prevTime?)`
|
||||
|
||||
Only available in Node.js, mimicks process.hrtime().
|
||||
|
||||
### `clock.nextTick(callback)`
|
||||
|
||||
Only available in Node.js, mimics `process.nextTick` to enable completely synchronous testing flows.
|
||||
|
||||
### `clock.performance.now()`
|
||||
|
||||
Only available in browser environments, mimicks performance.now().
|
||||
|
||||
### `clock.tick(time)` / `await clock.tickAsync(time)`
|
||||
|
||||
Advance the clock, firing callbacks if necessary. `time` may be the number of
|
||||
milliseconds to advance the clock by or a human-readable string. Valid string
|
||||
formats are `"08"` for eight seconds, `"01:00"` for one minute and `"02:34:10"`
|
||||
for two hours, 34 minutes and ten seconds.
|
||||
|
||||
The `tickAsync()` will also break the event loop, allowing any scheduled promise
|
||||
callbacks to execute _before_ running the timers.
|
||||
|
||||
### `clock.next()` / `await clock.nextAsync()`
|
||||
|
||||
Advances the clock to the the moment of the first scheduled timer, firing it.
|
||||
|
||||
The `nextAsync()` will also break the event loop, allowing any scheduled promise
|
||||
callbacks to execute _before_ running the timers.
|
||||
|
||||
### `clock.jump(time)`
|
||||
|
||||
Advance the clock by jumping forward in time, firing callbacks at most once.
|
||||
`time` takes the same formats as [`clock.tick`](#clockticktime--await-clocktickasynctime).
|
||||
|
||||
This can be used to simulate the JS engine (such as a browser) being put to sleep and resumed later, skipping intermediary timers.
|
||||
|
||||
### `clock.reset()`
|
||||
|
||||
Removes all timers and ticks without firing them, and sets `now` to `config.now`
|
||||
that was provided to `FakeTimers.install` or to `0` if `config.now` was not provided.
|
||||
Useful to reset the state of the clock without having to `uninstall` and `install` it.
|
||||
|
||||
### `clock.runAll()` / `await clock.runAllAsync()`
|
||||
|
||||
This runs all pending timers until there are none remaining. If new timers are added while it is executing they will be run as well.
|
||||
|
||||
This makes it easier to run asynchronous tests to completion without worrying about the number of timers they use, or the delays in those timers.
|
||||
|
||||
It runs a maximum of `loopLimit` times after which it assumes there is an infinite loop of timers and throws an error.
|
||||
|
||||
The `runAllAsync()` will also break the event loop, allowing any scheduled promise
|
||||
callbacks to execute _before_ running the timers.
|
||||
|
||||
### `clock.runMicrotasks()`
|
||||
|
||||
This runs all pending microtasks scheduled with `nextTick` but none of the timers and is mostly useful for libraries using FakeTimers underneath and for running `nextTick` items without any timers.
|
||||
|
||||
### `clock.runToFrame()`
|
||||
|
||||
Advances the clock to the next frame, firing all scheduled animation frame callbacks,
|
||||
if any, for that frame as well as any other timers scheduled along the way.
|
||||
|
||||
### `clock.runToLast()` / `await clock.runToLastAsync()`
|
||||
|
||||
This takes note of the last scheduled timer when it is run, and advances the
|
||||
clock to that time firing callbacks as necessary.
|
||||
|
||||
If new timers are added while it is executing they will be run only if they
|
||||
would occur before this time.
|
||||
|
||||
This is useful when you want to run a test to completion, but the test recursively
|
||||
sets timers that would cause `runAll` to trigger an infinite loop warning.
|
||||
|
||||
The `runToLastAsync()` will also break the event loop, allowing any scheduled promise
|
||||
callbacks to execute _before_ running the timers.
|
||||
|
||||
### `clock.setSystemTime([now])`
|
||||
|
||||
This simulates a user changing the system clock while your program is running.
|
||||
It affects the current time but it does not in itself cause e.g. timers to fire;
|
||||
they will fire exactly as they would have done without the call to
|
||||
setSystemTime().
|
||||
|
||||
### `clock.uninstall()`
|
||||
|
||||
Restores the original methods of the native timers or the methods on the object
|
||||
that was passed to `FakeTimers.withGlobal`
|
||||
|
||||
### `Date`
|
||||
|
||||
Implements the `Date` object but using the clock to provide the correct time.
|
||||
|
||||
### `Performance`
|
||||
|
||||
Implements the `now` method of the [`Performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) object but using the clock to provide the correct time. Only available in environments that support the Performance object (browsers mostly).
|
||||
|
||||
### `FakeTimers.withGlobal`
|
||||
|
||||
In order to support creating clocks based on separate or sandboxed environments (such as JSDOM), FakeTimers exports a factory method which takes single argument `global`, which it inspects to figure out what to mock and what features to support. When invoking this function with a global, you will get back an object with `timers`, `createClock` and `install` - same as the regular FakeTimers exports only based on the passed in global instead of the global environment.
|
||||
|
||||
## Running tests
|
||||
|
||||
FakeTimers has a comprehensive test suite. If you're thinking of contributing bug
|
||||
fixes or suggesting new features, you need to make sure you have not broken any
|
||||
tests. You are also expected to add tests for any new behavior.
|
||||
|
||||
### On node:
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
Or, if you prefer more verbose output:
|
||||
|
||||
```
|
||||
$(npm bin)/mocha ./test/fake-timers-test.js
|
||||
```
|
||||
|
||||
### In the browser
|
||||
|
||||
[Mochify](https://github.com/mochify-js) is used to run the tests in headless
|
||||
Chrome.
|
||||
|
||||
```sh
|
||||
npm test-headless
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
BSD 3-clause "New" or "Revised" License (see LICENSE file)
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "@sinonjs/fake-timers",
|
||||
"description": "Fake JavaScript timers",
|
||||
"version": "11.3.1",
|
||||
"homepage": "https://github.com/sinonjs/fake-timers",
|
||||
"author": "Christian Johansen",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sinonjs/fake-timers.git"
|
||||
},
|
||||
"bugs": {
|
||||
"mail": "christian@cjohansen.no",
|
||||
"url": "https://github.com/sinonjs/fake-timers/issues"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test-node": "mocha --timeout 200 test/ integration-test/ -R dot --check-leaks",
|
||||
"test-headless": "mochify --driver puppeteer",
|
||||
"test-check-coverage": "npm run test-coverage && nyc check-coverage",
|
||||
"test-cloud": "npm run test-edge && npm run test-firefox && npm run test-safari",
|
||||
"test-edge": "BROWSER_NAME=MicrosoftEdge mochify --config mochify.webdriver.js",
|
||||
"test-firefox": "BROWSER_NAME=firefox mochify --config mochify.webdriver.js",
|
||||
"test-safari": "BROWSER_NAME=safari mochify --config mochify.webdriver.js",
|
||||
"test-coverage": "nyc -x mochify.webdriver.js -x coverage --all --reporter text --reporter html --reporter lcovonly npm run test-node",
|
||||
"test": "npm run test-node && npm run test-headless",
|
||||
"prettier:check": "prettier --check '**/*.{js,css,md}'",
|
||||
"prettier:write": "prettier --write '**/*.{js,css,md}'",
|
||||
"preversion": "./scripts/preversion.sh",
|
||||
"version": "./scripts/version.sh",
|
||||
"postversion": "./scripts/postversion.sh",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,css,md}": "prettier --check",
|
||||
"*.js": "eslint"
|
||||
},
|
||||
"mochify": {
|
||||
"reporter": "dot",
|
||||
"timeout": 10000,
|
||||
"bundle": "esbuild --bundle --sourcemap=inline --define:process.env.NODE_DEBUG=\"\"",
|
||||
"bundle_stdin": "require",
|
||||
"spec": "test/**/*-test.js"
|
||||
},
|
||||
"files": [
|
||||
"src/"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@mochify/cli": "^0.4.1",
|
||||
"@mochify/driver-puppeteer": "^0.4.0",
|
||||
"@mochify/driver-webdriver": "^0.2.1",
|
||||
"@sinonjs/eslint-config": "^5.0.3",
|
||||
"@sinonjs/referee-sinon": "12.0.0",
|
||||
"esbuild": "^0.23.1",
|
||||
"husky": "^9.1.5",
|
||||
"jsdom": "24.1.1",
|
||||
"lint-staged": "15.2.9",
|
||||
"mocha": "10.7.3",
|
||||
"nyc": "17.0.0",
|
||||
"prettier": "3.3.3"
|
||||
},
|
||||
"main": "./src/fake-timers-src.js",
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.1"
|
||||
},
|
||||
"nyc": {
|
||||
"branches": 85,
|
||||
"lines": 92,
|
||||
"functions": 92,
|
||||
"statements": 92,
|
||||
"exclude": [
|
||||
"**/*-test.js",
|
||||
"coverage/**",
|
||||
"types/**",
|
||||
"fake-timers.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
+2152
File diff suppressed because it is too large
Load Diff
+27
@@ -0,0 +1,27 @@
|
||||
(The BSD License)
|
||||
|
||||
Copyright (c) 2010-2012, Christian Johansen, christian@cjohansen.no and
|
||||
August Lilleaas, august.lilleaas@gmail.com. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Christian Johansen nor the names of his contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# samsam
|
||||
|
||||
[](https://circleci.com/gh/sinonjs/samsam)
|
||||
[](https://codecov.io/gh/sinonjs/samsam)
|
||||
<a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
|
||||
|
||||
Value identification and comparison functions
|
||||
|
||||
Documentation: http://sinonjs.github.io/samsam/
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/sinon#backer)]
|
||||
|
||||
<a href="https://opencollective.com/sinon/backer/0/website" target="_blank"><img src="https://opencollective.com/sinon/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/1/website" target="_blank"><img src="https://opencollective.com/sinon/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/2/website" target="_blank"><img src="https://opencollective.com/sinon/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/3/website" target="_blank"><img src="https://opencollective.com/sinon/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/4/website" target="_blank"><img src="https://opencollective.com/sinon/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/5/website" target="_blank"><img src="https://opencollective.com/sinon/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/6/website" target="_blank"><img src="https://opencollective.com/sinon/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/7/website" target="_blank"><img src="https://opencollective.com/sinon/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/8/website" target="_blank"><img src="https://opencollective.com/sinon/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/9/website" target="_blank"><img src="https://opencollective.com/sinon/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/10/website" target="_blank"><img src="https://opencollective.com/sinon/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/11/website" target="_blank"><img src="https://opencollective.com/sinon/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/12/website" target="_blank"><img src="https://opencollective.com/sinon/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/13/website" target="_blank"><img src="https://opencollective.com/sinon/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/14/website" target="_blank"><img src="https://opencollective.com/sinon/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/15/website" target="_blank"><img src="https://opencollective.com/sinon/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/16/website" target="_blank"><img src="https://opencollective.com/sinon/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/17/website" target="_blank"><img src="https://opencollective.com/sinon/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/18/website" target="_blank"><img src="https://opencollective.com/sinon/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/19/website" target="_blank"><img src="https://opencollective.com/sinon/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/20/website" target="_blank"><img src="https://opencollective.com/sinon/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/21/website" target="_blank"><img src="https://opencollective.com/sinon/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/22/website" target="_blank"><img src="https://opencollective.com/sinon/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/23/website" target="_blank"><img src="https://opencollective.com/sinon/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/24/website" target="_blank"><img src="https://opencollective.com/sinon/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/25/website" target="_blank"><img src="https://opencollective.com/sinon/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/26/website" target="_blank"><img src="https://opencollective.com/sinon/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/27/website" target="_blank"><img src="https://opencollective.com/sinon/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/28/website" target="_blank"><img src="https://opencollective.com/sinon/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/backer/29/website" target="_blank"><img src="https://opencollective.com/sinon/backer/29/avatar.svg"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/sinon#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/sinon/sponsor/0/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/1/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/2/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/3/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/4/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/5/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/6/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/7/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/8/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/9/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/10/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/11/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/12/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/13/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/14/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/15/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/16/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/17/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/18/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/19/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/20/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/21/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/22/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/23/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/24/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/25/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/26/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/27/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/28/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/sinon/sponsor/29/website" target="_blank"><img src="https://opencollective.com/sinon/sponsor/29/avatar.svg"></a>
|
||||
|
||||
## Licence
|
||||
|
||||
samsam was released under [BSD-3](LICENSE)
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
# samsam
|
||||
|
||||
> Same same, but different
|
||||
|
||||
`samsam` is a collection of predicate and comparison functions useful for
|
||||
identifiying the type of values and to compare values with varying degrees of
|
||||
strictness.
|
||||
|
||||
`samsam` is a general-purpose library. It works in browsers and Node. It will
|
||||
define itself as an AMD module if you want it to (i.e. if there's a `define`
|
||||
function available).
|
||||
|
||||
## Predicate functions
|
||||
|
||||
### `isArguments(value)`
|
||||
|
||||
Returns `true` if `value` is an `arguments` object, `false` otherwise.
|
||||
|
||||
### `isNegZero(value)`
|
||||
|
||||
Returns `true` if `value` is `-0`.
|
||||
|
||||
### `isElement(value)`
|
||||
|
||||
Returns `true` if `value` is a DOM element node. Unlike
|
||||
Underscore.js/lodash, this function will return `false` if `value` is an
|
||||
_element-like_ object, i.e. a regular object with a `nodeType` property that
|
||||
holds the value `1`.
|
||||
|
||||
### `isSet(value)`
|
||||
|
||||
Returns `true` if `value` is a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
|
||||
|
||||
## Comparison functions
|
||||
|
||||
### `identical(x, y)`
|
||||
|
||||
Strict equality check according to EcmaScript Harmony's `egal`.
|
||||
|
||||
**From the Harmony wiki:**
|
||||
|
||||
> An egal function simply makes available the internal `SameValue` function
|
||||
> from section 9.12 of the ES5 spec. If two values are egal, then they are not
|
||||
> observably distinguishable.
|
||||
|
||||
`identical` returns `true` when `===` is `true`, except for `-0` and
|
||||
`+0`, where it returns `false`. Additionally, it returns `true` when
|
||||
`NaN` is compared to itself.
|
||||
|
||||
### `deepEqual(actual, expectation)`
|
||||
|
||||
Deep equal comparison. Two values are "deep equal" if:
|
||||
|
||||
- They are identical
|
||||
- They are both date objects representing the same time
|
||||
- They are both arrays containing elements that are all deepEqual
|
||||
- They are objects with the same set of properties, and each property
|
||||
in `actual` is deepEqual to the corresponding property in `expectation`
|
||||
|
||||
- `actual` can have [symbolic properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) that are missing from `expectation`
|
||||
|
||||
### Matcher
|
||||
|
||||
Match values and objects by type or or other fuzzy criteria. `samsam` ships
|
||||
with these built in matchers:
|
||||
|
||||
#### `sinon.match.any`
|
||||
|
||||
Matches anything.
|
||||
|
||||
#### `sinon.match.defined`
|
||||
|
||||
Requires the value to be defined.
|
||||
|
||||
#### `sinon.match.truthy`
|
||||
|
||||
Requires the value to be truthy.
|
||||
|
||||
#### `sinon.match.falsy`
|
||||
|
||||
Requires the value to be falsy.
|
||||
|
||||
#### `sinon.match.bool`
|
||||
|
||||
Requires the value to be a `Boolean`
|
||||
|
||||
#### `sinon.match.number`
|
||||
|
||||
Requires the value to be a `Number`.
|
||||
|
||||
#### `sinon.match.string`
|
||||
|
||||
Requires the value to be a `String`.
|
||||
|
||||
#### `sinon.match.object`
|
||||
|
||||
Requires the value to be an `Object`.
|
||||
|
||||
#### `sinon.match.func`
|
||||
|
||||
Requires the value to be a `Function`.
|
||||
|
||||
#### `sinon.match.array`
|
||||
|
||||
Requires the value to be an `Array`.
|
||||
|
||||
#### `sinon.match.array.deepEquals(arr)`
|
||||
|
||||
Requires an `Array` to be deep equal another one.
|
||||
|
||||
#### `sinon.match.array.startsWith(arr)`
|
||||
|
||||
Requires an `Array` to start with the same values as another one.
|
||||
|
||||
#### `sinon.match.array.endsWith(arr)`
|
||||
|
||||
Requires an `Array` to end with the same values as another one.
|
||||
|
||||
#### `sinon.match.array.contains(arr)`
|
||||
|
||||
Requires an `Array` to contain each one of the values the given array has.
|
||||
|
||||
#### `sinon.match.map`
|
||||
|
||||
Requires the value to be a `Map`.
|
||||
|
||||
#### `sinon.match.map.deepEquals(map)`
|
||||
|
||||
Requires a `Map` to be deep equal another one.
|
||||
|
||||
#### `sinon.match.map.contains(map)`
|
||||
|
||||
Requires a `Map` to contain each one of the items the given map has.
|
||||
|
||||
#### `sinon.match.set`
|
||||
|
||||
Requires the value to be a `Set`.
|
||||
|
||||
#### `sinon.match.set.deepEquals(set)`
|
||||
|
||||
Requires a `Set` to be deep equal another one.
|
||||
|
||||
#### `sinon.match.set.contains(set)`
|
||||
|
||||
Requires a `Set` to contain each one of the items the given set has.
|
||||
|
||||
#### `sinon.match.regexp`
|
||||
|
||||
Requires the value to be a regular expression.
|
||||
|
||||
#### `sinon.match.date`
|
||||
|
||||
Requires the value to be a `Date` object.
|
||||
|
||||
#### `sinon.match.symbol`
|
||||
|
||||
Requires the value to be a `Symbol`.
|
||||
|
||||
#### `sinon.match.in(array)`
|
||||
|
||||
Requires the value to be in the `array`.
|
||||
|
||||
#### `sinon.match.same(ref)`
|
||||
|
||||
Requires the value to strictly equal `ref`.
|
||||
|
||||
#### `sinon.match.typeOf(type)`
|
||||
|
||||
Requires the value to be of the given type, where `type` can be one of
|
||||
`"undefined"`,
|
||||
`"null"`,
|
||||
`"boolean"`,
|
||||
`"number"`,
|
||||
`"string"`,
|
||||
`"object"`,
|
||||
`"function"`,
|
||||
`"array"`,
|
||||
`"regexp"`,
|
||||
`"date"` or
|
||||
`"symbol"`.
|
||||
|
||||
#### `sinon.match.instanceOf(type)`
|
||||
|
||||
Requires the value to be an instance of the given `type`.
|
||||
|
||||
#### `sinon.match.has(property[, expectation])`
|
||||
|
||||
Requires the value to define the given `property`.
|
||||
|
||||
The property might be inherited via the prototype chain. If the optional expectation is given, the value of the property is deeply compared with the expectation. The expectation can be another matcher.
|
||||
|
||||
#### `sinon.match.hasOwn(property[, expectation])`
|
||||
|
||||
Same as `sinon.match.has` but the property must be defined by the value itself. Inherited properties are ignored.
|
||||
|
||||
#### `sinon.match.hasNested(propertyPath[, expectation])`
|
||||
|
||||
Requires the value to define the given `propertyPath`. Dot (`prop.prop`) and bracket (`prop[0]`) notations are supported as in [Lodash.get](https://lodash.com/docs/4.4.2#get).
|
||||
|
||||
The propertyPath might be inherited via the prototype chain. If the optional expectation is given, the value at the propertyPath is deeply compared with the expectation. The expectation can be another matcher.
|
||||
|
||||
```javascript
|
||||
sinon.match.hasNested("a[0].b.c");
|
||||
|
||||
// Where actual is something like
|
||||
var actual = { a: [{ b: { c: 3 } }] };
|
||||
|
||||
sinon.match.hasNested("a.b.c");
|
||||
|
||||
// Where actual is something like
|
||||
var actual = { a: { b: { c: 3 } } };
|
||||
```
|
||||
|
||||
#### `sinon.match.every(matcher)`
|
||||
|
||||
Requires **every** element of an `Array`, `Set` or `Map`, or alternatively **every** value of an `Object` to match the given `matcher`.
|
||||
|
||||
#### `sinon.match.some(matcher)`
|
||||
|
||||
Requires **any** element of an `Array`, `Set` or `Map`, or alternatively **any** value of an `Object` to match the given `matcher`.
|
||||
|
||||
## Combining matchers
|
||||
|
||||
All matchers implement `and` and `or`. This allows to logically combine mutliple matchers. The result is a new matchers that requires both (and) or one of the matchers (or) to return `true`.
|
||||
|
||||
```javascript
|
||||
var stringOrNumber = sinon.match.string.or(sinon.match.number);
|
||||
var bookWithPages = sinon.match.instanceOf(Book).and(sinon.match.has("pages"));
|
||||
```
|
||||
|
||||
### `match(object, matcher)`
|
||||
|
||||
Creates a custom matcher to perform partial equality check. Compares `object`
|
||||
with matcher according a wide set of rules:
|
||||
|
||||
#### String matcher
|
||||
|
||||
In its simplest form, `match` performs a case insensitive substring match.
|
||||
When the matcher is a string, `object` is converted to a string, and the
|
||||
function returns `true` if the matcher is a case-insensitive substring of
|
||||
`object` as a string.
|
||||
|
||||
```javascript
|
||||
samsam.match("Give me something", "Give"); //true
|
||||
samsam.match("Give me something", "sumptn"); // false
|
||||
samsam.match(
|
||||
{
|
||||
toString: function () {
|
||||
return "yeah";
|
||||
},
|
||||
},
|
||||
"Yeah!",
|
||||
); // true
|
||||
```
|
||||
|
||||
The last example is not symmetric. When the matcher is a string, the `object`
|
||||
is coerced to a string - in this case using `toString`. Changing the order of
|
||||
the arguments would cause the matcher to be an object, in which case different
|
||||
rules apply (see below).
|
||||
|
||||
#### Boolean matcher
|
||||
|
||||
Performs a strict (i.e. `===`) match with the object. So, only `true`
|
||||
matches `true`, and only `false` matches `false`.
|
||||
|
||||
#### Regular expression matcher
|
||||
|
||||
When the matcher is a regular expression, the function will pass if
|
||||
`object.test(matcher)` is `true`. `match` is written in a generic way, so
|
||||
any object with a `test` method will be used as a matcher this way.
|
||||
|
||||
```javascript
|
||||
samsam.match("Give me something", /^[a-z\s]$/i); // true
|
||||
samsam.match("Give me something", /[0-9]/); // false
|
||||
samsam.match(
|
||||
{
|
||||
toString: function () {
|
||||
return "yeah!";
|
||||
},
|
||||
},
|
||||
/yeah/,
|
||||
); // true
|
||||
samsam.match(234, /[a-z]/); // false
|
||||
```
|
||||
|
||||
#### Number matcher
|
||||
|
||||
When the matcher is a number, the assertion will pass if `object == matcher`.
|
||||
|
||||
```javascript
|
||||
samsam.match("123", 123); // true
|
||||
samsam.match("Give me something", 425); // false
|
||||
samsam.match(
|
||||
{
|
||||
toString: function () {
|
||||
return "42";
|
||||
},
|
||||
},
|
||||
42,
|
||||
); // true
|
||||
samsam.match(234, 1234); // false
|
||||
```
|
||||
|
||||
#### Function matcher
|
||||
|
||||
When the matcher is a function, it is called with `object` as its only
|
||||
argument. `match` returns `true` if the function returns `true`. A strict
|
||||
match is performed against the return value, so a boolean `true` is required,
|
||||
truthy is not enough.
|
||||
|
||||
```javascript
|
||||
// true
|
||||
samsam.match("123", function (exp) {
|
||||
return exp == "123";
|
||||
});
|
||||
|
||||
// false
|
||||
samsam.match("Give me something", function () {
|
||||
return "ok";
|
||||
});
|
||||
|
||||
// true
|
||||
samsam.match(
|
||||
{
|
||||
toString: function () {
|
||||
return "42";
|
||||
},
|
||||
},
|
||||
function () {
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
// false
|
||||
samsam.match(234, function () {});
|
||||
```
|
||||
|
||||
#### Object matcher
|
||||
|
||||
As mentioned above, if an object matcher defines a `test` method, `match`
|
||||
will return `true` if `matcher.test(object)` returns truthy.
|
||||
|
||||
If the matcher does not have a test method, a recursive match is performed. If
|
||||
all properties of `matcher` matches corresponding properties in `object`,
|
||||
`match` returns `true`. Note that the object matcher does not care if the
|
||||
number of properties in the two objects are the same - only if all properties in
|
||||
the matcher recursively matches ones in `object`. If supported, this object matchers
|
||||
include [symbolic properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
|
||||
in the comparison.
|
||||
|
||||
```javascript
|
||||
// true
|
||||
samsam.match("123", {
|
||||
test: function (arg) {
|
||||
return arg == 123;
|
||||
},
|
||||
});
|
||||
|
||||
// false
|
||||
samsam.match({}, { prop: 42 });
|
||||
|
||||
// true
|
||||
samsam.match(
|
||||
{
|
||||
name: "Chris",
|
||||
profession: "Programmer",
|
||||
},
|
||||
{
|
||||
name: "Chris",
|
||||
},
|
||||
);
|
||||
|
||||
// false
|
||||
samsam.match(234, { name: "Chris" });
|
||||
```
|
||||
|
||||
#### DOM elements
|
||||
|
||||
`match` can be very helpful when comparing DOM elements, because it allows
|
||||
you to compare several properties with one call:
|
||||
|
||||
```javascript
|
||||
var el = document.getElementById("myEl");
|
||||
|
||||
samsam.match(el, {
|
||||
tagName: "h2",
|
||||
className: "item",
|
||||
innerHTML: "Howdy",
|
||||
});
|
||||
```
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
var ARRAY_TYPES = [
|
||||
Array,
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
];
|
||||
|
||||
module.exports = ARRAY_TYPES;
|
||||
+413
@@ -0,0 +1,413 @@
|
||||
"use strict";
|
||||
|
||||
var arrayProto = require("@sinonjs/commons").prototypes.array;
|
||||
var deepEqual = require("./deep-equal").use(createMatcher); // eslint-disable-line no-use-before-define
|
||||
var every = require("@sinonjs/commons").every;
|
||||
var functionName = require("@sinonjs/commons").functionName;
|
||||
var get = require("lodash.get");
|
||||
var iterableToString = require("./iterable-to-string");
|
||||
var objectProto = require("@sinonjs/commons").prototypes.object;
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
var valueToString = require("@sinonjs/commons").valueToString;
|
||||
|
||||
var assertMatcher = require("./create-matcher/assert-matcher");
|
||||
var assertMethodExists = require("./create-matcher/assert-method-exists");
|
||||
var assertType = require("./create-matcher/assert-type");
|
||||
var isIterable = require("./create-matcher/is-iterable");
|
||||
var isMatcher = require("./create-matcher/is-matcher");
|
||||
|
||||
var matcherPrototype = require("./create-matcher/matcher-prototype");
|
||||
|
||||
var arrayIndexOf = arrayProto.indexOf;
|
||||
var some = arrayProto.some;
|
||||
|
||||
var hasOwnProperty = objectProto.hasOwnProperty;
|
||||
var objectToString = objectProto.toString;
|
||||
|
||||
var TYPE_MAP = require("./create-matcher/type-map")(createMatcher); // eslint-disable-line no-use-before-define
|
||||
|
||||
/**
|
||||
* Creates a matcher object for the passed expectation
|
||||
*
|
||||
* @alias module:samsam.createMatcher
|
||||
* @param {*} expectation An expecttation
|
||||
* @param {string} message A message for the expectation
|
||||
* @returns {object} A matcher object
|
||||
*/
|
||||
function createMatcher(expectation, message) {
|
||||
var m = Object.create(matcherPrototype);
|
||||
var type = typeOf(expectation);
|
||||
|
||||
if (message !== undefined && typeof message !== "string") {
|
||||
throw new TypeError("Message should be a string");
|
||||
}
|
||||
|
||||
if (arguments.length > 2) {
|
||||
throw new TypeError(
|
||||
`Expected 1 or 2 arguments, received ${arguments.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (type in TYPE_MAP) {
|
||||
TYPE_MAP[type](m, expectation, message);
|
||||
} else {
|
||||
m.test = function (actual) {
|
||||
return deepEqual(actual, expectation);
|
||||
};
|
||||
}
|
||||
|
||||
if (!m.message) {
|
||||
m.message = `match(${valueToString(expectation)})`;
|
||||
}
|
||||
|
||||
// ensure that nothing mutates the exported message value, ref https://github.com/sinonjs/sinon/issues/2502
|
||||
Object.defineProperty(m, "message", {
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: m.message,
|
||||
});
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
createMatcher.isMatcher = isMatcher;
|
||||
|
||||
createMatcher.any = createMatcher(function () {
|
||||
return true;
|
||||
}, "any");
|
||||
|
||||
createMatcher.defined = createMatcher(function (actual) {
|
||||
return actual !== null && actual !== undefined;
|
||||
}, "defined");
|
||||
|
||||
createMatcher.truthy = createMatcher(function (actual) {
|
||||
return Boolean(actual);
|
||||
}, "truthy");
|
||||
|
||||
createMatcher.falsy = createMatcher(function (actual) {
|
||||
return !actual;
|
||||
}, "falsy");
|
||||
|
||||
createMatcher.same = function (expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return expectation === actual;
|
||||
},
|
||||
`same(${valueToString(expectation)})`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.in = function (arrayOfExpectations) {
|
||||
if (typeOf(arrayOfExpectations) !== "array") {
|
||||
throw new TypeError("array expected");
|
||||
}
|
||||
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return some(arrayOfExpectations, function (expectation) {
|
||||
return expectation === actual;
|
||||
});
|
||||
},
|
||||
`in(${valueToString(arrayOfExpectations)})`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.typeOf = function (type) {
|
||||
assertType(type, "string", "type");
|
||||
return createMatcher(function (actual) {
|
||||
return typeOf(actual) === type;
|
||||
}, `typeOf("${type}")`);
|
||||
};
|
||||
|
||||
createMatcher.instanceOf = function (type) {
|
||||
/* istanbul ignore if */
|
||||
if (
|
||||
typeof Symbol === "undefined" ||
|
||||
typeof Symbol.hasInstance === "undefined"
|
||||
) {
|
||||
assertType(type, "function", "type");
|
||||
} else {
|
||||
assertMethodExists(
|
||||
type,
|
||||
Symbol.hasInstance,
|
||||
"type",
|
||||
"[Symbol.hasInstance]",
|
||||
);
|
||||
}
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return actual instanceof type;
|
||||
},
|
||||
`instanceOf(${functionName(type) || objectToString(type)})`,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a property matcher
|
||||
*
|
||||
* @private
|
||||
* @param {Function} propertyTest A function to test the property against a value
|
||||
* @param {string} messagePrefix A prefix to use for messages generated by the matcher
|
||||
* @returns {object} A matcher
|
||||
*/
|
||||
function createPropertyMatcher(propertyTest, messagePrefix) {
|
||||
return function (property, value) {
|
||||
assertType(property, "string", "property");
|
||||
var onlyProperty = arguments.length === 1;
|
||||
var message = `${messagePrefix}("${property}"`;
|
||||
if (!onlyProperty) {
|
||||
message += `, ${valueToString(value)}`;
|
||||
}
|
||||
message += ")";
|
||||
return createMatcher(function (actual) {
|
||||
if (
|
||||
actual === undefined ||
|
||||
actual === null ||
|
||||
!propertyTest(actual, property)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return onlyProperty || deepEqual(actual[property], value);
|
||||
}, message);
|
||||
};
|
||||
}
|
||||
|
||||
createMatcher.has = createPropertyMatcher(function (actual, property) {
|
||||
if (typeof actual === "object") {
|
||||
return property in actual;
|
||||
}
|
||||
return actual[property] !== undefined;
|
||||
}, "has");
|
||||
|
||||
createMatcher.hasOwn = createPropertyMatcher(function (actual, property) {
|
||||
return hasOwnProperty(actual, property);
|
||||
}, "hasOwn");
|
||||
|
||||
createMatcher.hasNested = function (property, value) {
|
||||
assertType(property, "string", "property");
|
||||
var onlyProperty = arguments.length === 1;
|
||||
var message = `hasNested("${property}"`;
|
||||
if (!onlyProperty) {
|
||||
message += `, ${valueToString(value)}`;
|
||||
}
|
||||
message += ")";
|
||||
return createMatcher(function (actual) {
|
||||
if (
|
||||
actual === undefined ||
|
||||
actual === null ||
|
||||
get(actual, property) === undefined
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return onlyProperty || deepEqual(get(actual, property), value);
|
||||
}, message);
|
||||
};
|
||||
|
||||
var jsonParseResultTypes = {
|
||||
null: true,
|
||||
boolean: true,
|
||||
number: true,
|
||||
string: true,
|
||||
object: true,
|
||||
array: true,
|
||||
};
|
||||
createMatcher.json = function (value) {
|
||||
if (!jsonParseResultTypes[typeOf(value)]) {
|
||||
throw new TypeError("Value cannot be the result of JSON.parse");
|
||||
}
|
||||
var message = `json(${JSON.stringify(value, null, " ")})`;
|
||||
return createMatcher(function (actual) {
|
||||
var parsed;
|
||||
try {
|
||||
parsed = JSON.parse(actual);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return deepEqual(parsed, value);
|
||||
}, message);
|
||||
};
|
||||
|
||||
createMatcher.every = function (predicate) {
|
||||
assertMatcher(predicate);
|
||||
|
||||
return createMatcher(function (actual) {
|
||||
if (typeOf(actual) === "object") {
|
||||
return every(Object.keys(actual), function (key) {
|
||||
return predicate.test(actual[key]);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
isIterable(actual) &&
|
||||
every(actual, function (element) {
|
||||
return predicate.test(element);
|
||||
})
|
||||
);
|
||||
}, `every(${predicate.message})`);
|
||||
};
|
||||
|
||||
createMatcher.some = function (predicate) {
|
||||
assertMatcher(predicate);
|
||||
|
||||
return createMatcher(function (actual) {
|
||||
if (typeOf(actual) === "object") {
|
||||
return !every(Object.keys(actual), function (key) {
|
||||
return !predicate.test(actual[key]);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
isIterable(actual) &&
|
||||
!every(actual, function (element) {
|
||||
return !predicate.test(element);
|
||||
})
|
||||
);
|
||||
}, `some(${predicate.message})`);
|
||||
};
|
||||
|
||||
createMatcher.array = createMatcher.typeOf("array");
|
||||
|
||||
createMatcher.array.deepEquals = function (expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||||
var sameLength = actual.length === expectation.length;
|
||||
return (
|
||||
typeOf(actual) === "array" &&
|
||||
sameLength &&
|
||||
every(actual, function (element, index) {
|
||||
var expected = expectation[index];
|
||||
return typeOf(expected) === "array" &&
|
||||
typeOf(element) === "array"
|
||||
? createMatcher.array.deepEquals(expected).test(element)
|
||||
: deepEqual(expected, element);
|
||||
})
|
||||
);
|
||||
},
|
||||
`deepEquals([${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.array.startsWith = function (expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return (
|
||||
typeOf(actual) === "array" &&
|
||||
every(expectation, function (expectedElement, index) {
|
||||
return actual[index] === expectedElement;
|
||||
})
|
||||
);
|
||||
},
|
||||
`startsWith([${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.array.endsWith = function (expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
// This indicates the index in which we should start matching
|
||||
var offset = actual.length - expectation.length;
|
||||
|
||||
return (
|
||||
typeOf(actual) === "array" &&
|
||||
every(expectation, function (expectedElement, index) {
|
||||
return actual[offset + index] === expectedElement;
|
||||
})
|
||||
);
|
||||
},
|
||||
`endsWith([${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.array.contains = function (expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return (
|
||||
typeOf(actual) === "array" &&
|
||||
every(expectation, function (expectedElement) {
|
||||
return arrayIndexOf(actual, expectedElement) !== -1;
|
||||
})
|
||||
);
|
||||
},
|
||||
`contains([${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.map = createMatcher.typeOf("map");
|
||||
|
||||
createMatcher.map.deepEquals = function mapDeepEquals(expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||||
var sameLength = actual.size === expectation.size;
|
||||
return (
|
||||
typeOf(actual) === "map" &&
|
||||
sameLength &&
|
||||
every(actual, function (element, key) {
|
||||
return (
|
||||
expectation.has(key) && expectation.get(key) === element
|
||||
);
|
||||
})
|
||||
);
|
||||
},
|
||||
`deepEquals(Map[${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.map.contains = function mapContains(expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return (
|
||||
typeOf(actual) === "map" &&
|
||||
every(expectation, function (element, key) {
|
||||
return actual.has(key) && actual.get(key) === element;
|
||||
})
|
||||
);
|
||||
},
|
||||
`contains(Map[${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.set = createMatcher.typeOf("set");
|
||||
|
||||
createMatcher.set.deepEquals = function setDeepEquals(expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||||
var sameLength = actual.size === expectation.size;
|
||||
return (
|
||||
typeOf(actual) === "set" &&
|
||||
sameLength &&
|
||||
every(actual, function (element) {
|
||||
return expectation.has(element);
|
||||
})
|
||||
);
|
||||
},
|
||||
`deepEquals(Set[${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.set.contains = function setContains(expectation) {
|
||||
return createMatcher(
|
||||
function (actual) {
|
||||
return (
|
||||
typeOf(actual) === "set" &&
|
||||
every(expectation, function (element) {
|
||||
return actual.has(element);
|
||||
})
|
||||
);
|
||||
},
|
||||
`contains(Set[${iterableToString(expectation)}])`,
|
||||
);
|
||||
};
|
||||
|
||||
createMatcher.bool = createMatcher.typeOf("boolean");
|
||||
createMatcher.number = createMatcher.typeOf("number");
|
||||
createMatcher.string = createMatcher.typeOf("string");
|
||||
createMatcher.object = createMatcher.typeOf("object");
|
||||
createMatcher.func = createMatcher.typeOf("function");
|
||||
createMatcher.regexp = createMatcher.typeOf("regexp");
|
||||
createMatcher.date = createMatcher.typeOf("date");
|
||||
createMatcher.symbol = createMatcher.typeOf("symbol");
|
||||
|
||||
module.exports = createMatcher;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
var isMatcher = require("./is-matcher");
|
||||
|
||||
/**
|
||||
* Throws a TypeError when `value` is not a matcher
|
||||
*
|
||||
* @private
|
||||
* @param {*} value The value to examine
|
||||
*/
|
||||
function assertMatcher(value) {
|
||||
if (!isMatcher(value)) {
|
||||
throw new TypeError("Matcher expected");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = assertMatcher;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Throws a TypeError when expected method doesn't exist
|
||||
*
|
||||
* @private
|
||||
* @param {*} value A value to examine
|
||||
* @param {string} method The name of the method to look for
|
||||
* @param {name} name A name to use for the error message
|
||||
* @param {string} methodPath The name of the method to use for error messages
|
||||
* @throws {TypeError} When the method doesn't exist
|
||||
*/
|
||||
function assertMethodExists(value, method, name, methodPath) {
|
||||
if (value[method] === null || value[method] === undefined) {
|
||||
throw new TypeError(`Expected ${name} to have method ${methodPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = assertMethodExists;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
|
||||
/**
|
||||
* Ensures that value is of type
|
||||
*
|
||||
* @private
|
||||
* @param {*} value A value to examine
|
||||
* @param {string} type A basic JavaScript type to compare to, e.g. "object", "string"
|
||||
* @param {string} name A string to use for the error message
|
||||
* @throws {TypeError} If value is not of the expected type
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function assertType(value, type, name) {
|
||||
var actual = typeOf(value);
|
||||
if (actual !== type) {
|
||||
throw new TypeError(
|
||||
`Expected type of ${name} to be ${type}, but was ${actual}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = assertType;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
|
||||
/**
|
||||
* Returns `true` for iterables
|
||||
*
|
||||
* @private
|
||||
* @param {*} value A value to examine
|
||||
* @returns {boolean} Returns `true` when `value` looks like an iterable
|
||||
*/
|
||||
function isIterable(value) {
|
||||
return Boolean(value) && typeOf(value.forEach) === "function";
|
||||
}
|
||||
|
||||
module.exports = isIterable;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
var isPrototypeOf = require("@sinonjs/commons").prototypes.object.isPrototypeOf;
|
||||
|
||||
var matcherPrototype = require("./matcher-prototype");
|
||||
|
||||
/**
|
||||
* Returns `true` when `object` is a matcher
|
||||
*
|
||||
* @private
|
||||
* @param {*} object A value to examine
|
||||
* @returns {boolean} Returns `true` when `object` is a matcher
|
||||
*/
|
||||
function isMatcher(object) {
|
||||
return isPrototypeOf(matcherPrototype, object);
|
||||
}
|
||||
|
||||
module.exports = isMatcher;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
|
||||
var every = require("@sinonjs/commons").prototypes.array.every;
|
||||
var concat = require("@sinonjs/commons").prototypes.array.concat;
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
|
||||
var deepEqualFactory = require("../deep-equal").use;
|
||||
|
||||
var identical = require("../identical");
|
||||
var isMatcher = require("./is-matcher");
|
||||
|
||||
var keys = Object.keys;
|
||||
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||
|
||||
/**
|
||||
* Matches `actual` with `expectation`
|
||||
*
|
||||
* @private
|
||||
* @param {*} actual A value to examine
|
||||
* @param {object} expectation An object with properties to match on
|
||||
* @param {object} matcher A matcher to use for comparison
|
||||
* @returns {boolean} Returns true when `actual` matches all properties in `expectation`
|
||||
*/
|
||||
function matchObject(actual, expectation, matcher) {
|
||||
var deepEqual = deepEqualFactory(matcher);
|
||||
if (actual === null || actual === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var expectedKeys = keys(expectation);
|
||||
/* istanbul ignore else: cannot collect coverage for engine that doesn't support Symbol */
|
||||
if (typeOf(getOwnPropertySymbols) === "function") {
|
||||
expectedKeys = concat(expectedKeys, getOwnPropertySymbols(expectation));
|
||||
}
|
||||
|
||||
return every(expectedKeys, function (key) {
|
||||
var exp = expectation[key];
|
||||
var act = actual[key];
|
||||
|
||||
if (isMatcher(exp)) {
|
||||
if (!exp.test(act)) {
|
||||
return false;
|
||||
}
|
||||
} else if (typeOf(exp) === "object") {
|
||||
if (identical(exp, act)) {
|
||||
return true;
|
||||
}
|
||||
if (!matchObject(act, exp, matcher)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!deepEqual(act, exp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = matchObject;
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
"use strict";
|
||||
|
||||
var matcherPrototype = {
|
||||
toString: function () {
|
||||
return this.message;
|
||||
},
|
||||
};
|
||||
|
||||
matcherPrototype.or = function (valueOrMatcher) {
|
||||
var createMatcher = require("../create-matcher");
|
||||
var isMatcher = createMatcher.isMatcher;
|
||||
|
||||
if (!arguments.length) {
|
||||
throw new TypeError("Matcher expected");
|
||||
}
|
||||
|
||||
var m2 = isMatcher(valueOrMatcher)
|
||||
? valueOrMatcher
|
||||
: createMatcher(valueOrMatcher);
|
||||
var m1 = this;
|
||||
var or = Object.create(matcherPrototype);
|
||||
or.test = function (actual) {
|
||||
return m1.test(actual) || m2.test(actual);
|
||||
};
|
||||
or.message = `${m1.message}.or(${m2.message})`;
|
||||
return or;
|
||||
};
|
||||
|
||||
matcherPrototype.and = function (valueOrMatcher) {
|
||||
var createMatcher = require("../create-matcher");
|
||||
var isMatcher = createMatcher.isMatcher;
|
||||
|
||||
if (!arguments.length) {
|
||||
throw new TypeError("Matcher expected");
|
||||
}
|
||||
|
||||
var m2 = isMatcher(valueOrMatcher)
|
||||
? valueOrMatcher
|
||||
: createMatcher(valueOrMatcher);
|
||||
var m1 = this;
|
||||
var and = Object.create(matcherPrototype);
|
||||
and.test = function (actual) {
|
||||
return m1.test(actual) && m2.test(actual);
|
||||
};
|
||||
and.message = `${m1.message}.and(${m2.message})`;
|
||||
return and;
|
||||
};
|
||||
|
||||
module.exports = matcherPrototype;
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
var functionName = require("@sinonjs/commons").functionName;
|
||||
var join = require("@sinonjs/commons").prototypes.array.join;
|
||||
var map = require("@sinonjs/commons").prototypes.array.map;
|
||||
var stringIndexOf = require("@sinonjs/commons").prototypes.string.indexOf;
|
||||
var valueToString = require("@sinonjs/commons").valueToString;
|
||||
|
||||
var matchObject = require("./match-object");
|
||||
|
||||
var createTypeMap = function (match) {
|
||||
return {
|
||||
function: function (m, expectation, message) {
|
||||
m.test = expectation;
|
||||
m.message = message || `match(${functionName(expectation)})`;
|
||||
},
|
||||
number: function (m, expectation) {
|
||||
m.test = function (actual) {
|
||||
// we need type coercion here
|
||||
return expectation == actual; // eslint-disable-line eqeqeq
|
||||
};
|
||||
},
|
||||
object: function (m, expectation) {
|
||||
var array = [];
|
||||
|
||||
if (typeof expectation.test === "function") {
|
||||
m.test = function (actual) {
|
||||
return expectation.test(actual) === true;
|
||||
};
|
||||
m.message = `match(${functionName(expectation.test)})`;
|
||||
return m;
|
||||
}
|
||||
|
||||
array = map(Object.keys(expectation), function (key) {
|
||||
return `${key}: ${valueToString(expectation[key])}`;
|
||||
});
|
||||
|
||||
m.test = function (actual) {
|
||||
return matchObject(actual, expectation, match);
|
||||
};
|
||||
m.message = `match(${join(array, ", ")})`;
|
||||
|
||||
return m;
|
||||
},
|
||||
regexp: function (m, expectation) {
|
||||
m.test = function (actual) {
|
||||
return typeof actual === "string" && expectation.test(actual);
|
||||
};
|
||||
},
|
||||
string: function (m, expectation) {
|
||||
m.test = function (actual) {
|
||||
return (
|
||||
typeof actual === "string" &&
|
||||
stringIndexOf(actual, expectation) !== -1
|
||||
);
|
||||
};
|
||||
m.message = `match("${expectation}")`;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = createTypeMap;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
var forEach = require("@sinonjs/commons").prototypes.array.forEach;
|
||||
|
||||
/**
|
||||
* This helper makes it convenient to create Set instances from a
|
||||
* collection, an overcomes the shortcoming that IE11 doesn't support
|
||||
* collection arguments
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array An array to create a set from
|
||||
* @returns {Set} A set (unique) containing the members from array
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
||||
*/
|
||||
function createSet(array) {
|
||||
if (arguments.length > 0 && !Array.isArray(array)) {
|
||||
throw new TypeError(
|
||||
"createSet can be called with either no arguments or an Array",
|
||||
);
|
||||
}
|
||||
|
||||
var items = typeOf(array) === "array" ? array : [];
|
||||
var set = new Set();
|
||||
|
||||
forEach(items, function (item) {
|
||||
set.add(item);
|
||||
});
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
module.exports = createSet;
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
|
||||
var Benchmark = require("benchmark");
|
||||
var deepEqual = require("./deep-equal");
|
||||
|
||||
var suite = new Benchmark.Suite();
|
||||
var complex1 = {
|
||||
"1e116061-59bf-433a-8ab0-017b67a51d26":
|
||||
"a7fd22ab-e809-414f-ad55-9c97598395d8",
|
||||
"3824e8b7-22f5-489c-9919-43b432e3af6b":
|
||||
"548baefd-f43c-4dc9-9df5-f7c9c96223b0",
|
||||
"123e5750-eb66-45e5-a770-310879203b33":
|
||||
"89ff817d-65a2-4598-b190-21c128096e6a",
|
||||
"1d66be95-8aaa-4167-9a47-e7ee19bb0735":
|
||||
"64349492-56e8-4100-9552-a89fb4a9aef4",
|
||||
"f5538565-dc92-4ee4-a762-1ba5fe0528f6": {
|
||||
"53631f78-2f2a-448f-89c7-ed3585e8e6f0":
|
||||
"2cce00ee-f5ee-43ef-878f-958597b23225",
|
||||
"73e8298b-72fd-4969-afc1-d891b61e744f":
|
||||
"4e57aa30-af51-4d78-887c-019755e5d117",
|
||||
"85439907-5b0e-4a08-8cfa-902a68dc3cc0":
|
||||
"9639add9-6897-4cf0-b3d3-2ebf9c214f01",
|
||||
"d4ae9d87-bd6c-47e0-95a1-6f4eb4211549":
|
||||
"41fd3dd2-43ce-47f2-b92e-462474d07a6f",
|
||||
"f70345a2-0ea3-45a6-bafa-8c7a72379277": {
|
||||
"1bce714b-cd0a-417d-9a0c-bf4b7d35c0c4":
|
||||
"3b8b0dde-e2ed-4b34-ac8d-729ba3c9667e",
|
||||
"13e05c60-97d1-43f0-a6ef-d5247f4dd11f":
|
||||
"60f685a4-6558-4ade-9d4b-28281c3989db",
|
||||
"925b2609-e7b7-42f5-82cf-2d995697cec5":
|
||||
"79115261-8161-4a6c-9487-47847276a717",
|
||||
"52d644ac-7b33-4b79-b5b3-5afe7fd4ec2c": [
|
||||
"3c2ae716-92f1-4a3d-b98f-50ea49f51c45",
|
||||
"de76b822-71b3-4b5a-a041-4140378b70e2",
|
||||
"0302a405-1d58-44fa-a0c6-dd07bb0ca26e",
|
||||
new Date(),
|
||||
new Error(),
|
||||
new RegExp(),
|
||||
// eslint-disable-next-line no-undef
|
||||
new Map(),
|
||||
new Set(),
|
||||
// eslint-disable-next-line no-undef
|
||||
new WeakMap(),
|
||||
// eslint-disable-next-line no-undef
|
||||
new WeakSet(),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
var complex2 = Object.create(complex1);
|
||||
|
||||
var cyclic1 = {
|
||||
"4a092cd1-225e-4739-8331-d6564aafb702":
|
||||
"d0cebbe0-23fb-4cc4-8fa0-ef11ceedf12e",
|
||||
};
|
||||
cyclic1.cyclicRef = cyclic1;
|
||||
|
||||
var cyclic2 = Object.create(cyclic1);
|
||||
|
||||
// add tests
|
||||
suite
|
||||
.add("complex objects", function () {
|
||||
return deepEqual(complex1, complex2);
|
||||
})
|
||||
.add("cyclic references", function () {
|
||||
return deepEqual(cyclic1, cyclic2);
|
||||
})
|
||||
// add listeners
|
||||
.on("cycle", function (event) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(String(event.target));
|
||||
})
|
||||
// run async
|
||||
.run({ async: true });
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
"use strict";
|
||||
|
||||
var valueToString = require("@sinonjs/commons").valueToString;
|
||||
var className = require("@sinonjs/commons").className;
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
var arrayProto = require("@sinonjs/commons").prototypes.array;
|
||||
var objectProto = require("@sinonjs/commons").prototypes.object;
|
||||
var mapForEach = require("@sinonjs/commons").prototypes.map.forEach;
|
||||
|
||||
var getClass = require("./get-class");
|
||||
var identical = require("./identical");
|
||||
var isArguments = require("./is-arguments");
|
||||
var isArrayType = require("./is-array-type");
|
||||
var isDate = require("./is-date");
|
||||
var isElement = require("./is-element");
|
||||
var isIterable = require("./is-iterable");
|
||||
var isMap = require("./is-map");
|
||||
var isNaN = require("./is-nan");
|
||||
var isObject = require("./is-object");
|
||||
var isSet = require("./is-set");
|
||||
var isSubset = require("./is-subset");
|
||||
|
||||
var concat = arrayProto.concat;
|
||||
var every = arrayProto.every;
|
||||
var push = arrayProto.push;
|
||||
|
||||
var getTime = Date.prototype.getTime;
|
||||
var hasOwnProperty = objectProto.hasOwnProperty;
|
||||
var indexOf = arrayProto.indexOf;
|
||||
var keys = Object.keys;
|
||||
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||
|
||||
/**
|
||||
* Deep equal comparison. Two values are "deep equal" when:
|
||||
*
|
||||
* - They are equal, according to samsam.identical
|
||||
* - They are both date objects representing the same time
|
||||
* - They are both arrays containing elements that are all deepEqual
|
||||
* - They are objects with the same set of properties, and each property
|
||||
* in ``actual`` is deepEqual to the corresponding property in ``expectation``
|
||||
*
|
||||
* Supports cyclic objects.
|
||||
*
|
||||
* @alias module:samsam.deepEqual
|
||||
* @param {*} actual The object to examine
|
||||
* @param {*} expectation The object actual is expected to be equal to
|
||||
* @param {object} match A value to match on
|
||||
* @returns {boolean} Returns true when actual and expectation are considered equal
|
||||
*/
|
||||
function deepEqualCyclic(actual, expectation, match) {
|
||||
// used for cyclic comparison
|
||||
// contain already visited objects
|
||||
var actualObjects = [];
|
||||
var expectationObjects = [];
|
||||
// contain pathes (position in the object structure)
|
||||
// of the already visited objects
|
||||
// indexes same as in objects arrays
|
||||
var actualPaths = [];
|
||||
var expectationPaths = [];
|
||||
// contains combinations of already compared objects
|
||||
// in the manner: { "$1['ref']$2['ref']": true }
|
||||
var compared = {};
|
||||
|
||||
// does the recursion for the deep equal check
|
||||
// eslint-disable-next-line complexity
|
||||
return (function deepEqual(
|
||||
actualObj,
|
||||
expectationObj,
|
||||
actualPath,
|
||||
expectationPath,
|
||||
) {
|
||||
// If both are matchers they must be the same instance in order to be
|
||||
// considered equal If we didn't do that we would end up running one
|
||||
// matcher against the other
|
||||
if (match && match.isMatcher(expectationObj)) {
|
||||
if (match.isMatcher(actualObj)) {
|
||||
return actualObj === expectationObj;
|
||||
}
|
||||
return expectationObj.test(actualObj);
|
||||
}
|
||||
|
||||
var actualType = typeof actualObj;
|
||||
var expectationType = typeof expectationObj;
|
||||
|
||||
if (
|
||||
actualObj === expectationObj ||
|
||||
isNaN(actualObj) ||
|
||||
isNaN(expectationObj) ||
|
||||
actualObj === null ||
|
||||
expectationObj === null ||
|
||||
actualObj === undefined ||
|
||||
expectationObj === undefined ||
|
||||
actualType !== "object" ||
|
||||
expectationType !== "object"
|
||||
) {
|
||||
return identical(actualObj, expectationObj);
|
||||
}
|
||||
|
||||
// Elements are only equal if identical(expected, actual)
|
||||
if (isElement(actualObj) || isElement(expectationObj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var isActualDate = isDate(actualObj);
|
||||
var isExpectationDate = isDate(expectationObj);
|
||||
if (isActualDate || isExpectationDate) {
|
||||
if (
|
||||
!isActualDate ||
|
||||
!isExpectationDate ||
|
||||
getTime.call(actualObj) !== getTime.call(expectationObj)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualObj instanceof RegExp && expectationObj instanceof RegExp) {
|
||||
if (valueToString(actualObj) !== valueToString(expectationObj)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualObj instanceof Promise && expectationObj instanceof Promise) {
|
||||
return actualObj === expectationObj;
|
||||
}
|
||||
|
||||
if (actualObj instanceof Error && expectationObj instanceof Error) {
|
||||
return actualObj === expectationObj;
|
||||
}
|
||||
|
||||
var actualClass = getClass(actualObj);
|
||||
var expectationClass = getClass(expectationObj);
|
||||
var actualKeys = keys(actualObj);
|
||||
var expectationKeys = keys(expectationObj);
|
||||
var actualName = className(actualObj);
|
||||
var expectationName = className(expectationObj);
|
||||
var expectationSymbols =
|
||||
typeOf(getOwnPropertySymbols) === "function"
|
||||
? getOwnPropertySymbols(expectationObj)
|
||||
: /* istanbul ignore next: cannot collect coverage for engine that doesn't support Symbol */
|
||||
[];
|
||||
var expectationKeysAndSymbols = concat(
|
||||
expectationKeys,
|
||||
expectationSymbols,
|
||||
);
|
||||
|
||||
if (isArguments(actualObj) || isArguments(expectationObj)) {
|
||||
if (actualObj.length !== expectationObj.length) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
actualType !== expectationType ||
|
||||
actualClass !== expectationClass ||
|
||||
actualKeys.length !== expectationKeys.length ||
|
||||
(actualName &&
|
||||
expectationName &&
|
||||
actualName !== expectationName)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSet(actualObj) || isSet(expectationObj)) {
|
||||
if (
|
||||
!isSet(actualObj) ||
|
||||
!isSet(expectationObj) ||
|
||||
actualObj.size !== expectationObj.size
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isSubset(actualObj, expectationObj, deepEqual);
|
||||
}
|
||||
|
||||
if (isMap(actualObj) || isMap(expectationObj)) {
|
||||
if (
|
||||
!isMap(actualObj) ||
|
||||
!isMap(expectationObj) ||
|
||||
actualObj.size !== expectationObj.size
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var mapsDeeplyEqual = true;
|
||||
mapForEach(actualObj, function (value, key) {
|
||||
mapsDeeplyEqual =
|
||||
mapsDeeplyEqual &&
|
||||
deepEqualCyclic(value, expectationObj.get(key));
|
||||
});
|
||||
|
||||
return mapsDeeplyEqual;
|
||||
}
|
||||
|
||||
// jQuery objects have iteration protocols
|
||||
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
|
||||
// But, they don't work well with the implementation concerning iterables below,
|
||||
// so we will detect them and use jQuery's own equality function
|
||||
/* istanbul ignore next -- this can only be tested in the `test-headless` script */
|
||||
if (
|
||||
actualObj.constructor &&
|
||||
actualObj.constructor.name === "jQuery" &&
|
||||
typeof actualObj.is === "function"
|
||||
) {
|
||||
return actualObj.is(expectationObj);
|
||||
}
|
||||
|
||||
var isActualNonArrayIterable =
|
||||
isIterable(actualObj) &&
|
||||
!isArrayType(actualObj) &&
|
||||
!isArguments(actualObj);
|
||||
var isExpectationNonArrayIterable =
|
||||
isIterable(expectationObj) &&
|
||||
!isArrayType(expectationObj) &&
|
||||
!isArguments(expectationObj);
|
||||
if (isActualNonArrayIterable || isExpectationNonArrayIterable) {
|
||||
var actualArray = Array.from(actualObj);
|
||||
var expectationArray = Array.from(expectationObj);
|
||||
if (actualArray.length !== expectationArray.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var arrayDeeplyEquals = true;
|
||||
every(actualArray, function (key) {
|
||||
arrayDeeplyEquals =
|
||||
arrayDeeplyEquals &&
|
||||
deepEqualCyclic(actualArray[key], expectationArray[key]);
|
||||
});
|
||||
|
||||
return arrayDeeplyEquals;
|
||||
}
|
||||
|
||||
return every(expectationKeysAndSymbols, function (key) {
|
||||
if (!hasOwnProperty(actualObj, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var actualValue = actualObj[key];
|
||||
var expectationValue = expectationObj[key];
|
||||
var actualObject = isObject(actualValue);
|
||||
var expectationObject = isObject(expectationValue);
|
||||
// determines, if the objects were already visited
|
||||
// (it's faster to check for isObject first, than to
|
||||
// get -1 from getIndex for non objects)
|
||||
var actualIndex = actualObject
|
||||
? indexOf(actualObjects, actualValue)
|
||||
: -1;
|
||||
var expectationIndex = expectationObject
|
||||
? indexOf(expectationObjects, expectationValue)
|
||||
: -1;
|
||||
// determines the new paths of the objects
|
||||
// - for non cyclic objects the current path will be extended
|
||||
// by current property name
|
||||
// - for cyclic objects the stored path is taken
|
||||
var newActualPath =
|
||||
actualIndex !== -1
|
||||
? actualPaths[actualIndex]
|
||||
: `${actualPath}[${JSON.stringify(key)}]`;
|
||||
var newExpectationPath =
|
||||
expectationIndex !== -1
|
||||
? expectationPaths[expectationIndex]
|
||||
: `${expectationPath}[${JSON.stringify(key)}]`;
|
||||
var combinedPath = newActualPath + newExpectationPath;
|
||||
|
||||
// stop recursion if current objects are already compared
|
||||
if (compared[combinedPath]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remember the current objects and their paths
|
||||
if (actualIndex === -1 && actualObject) {
|
||||
push(actualObjects, actualValue);
|
||||
push(actualPaths, newActualPath);
|
||||
}
|
||||
if (expectationIndex === -1 && expectationObject) {
|
||||
push(expectationObjects, expectationValue);
|
||||
push(expectationPaths, newExpectationPath);
|
||||
}
|
||||
|
||||
// remember that the current objects are already compared
|
||||
if (actualObject && expectationObject) {
|
||||
compared[combinedPath] = true;
|
||||
}
|
||||
|
||||
// End of cyclic logic
|
||||
|
||||
// neither actualValue nor expectationValue is a cycle
|
||||
// continue with next level
|
||||
return deepEqual(
|
||||
actualValue,
|
||||
expectationValue,
|
||||
newActualPath,
|
||||
newExpectationPath,
|
||||
);
|
||||
});
|
||||
})(actual, expectation, "$1", "$2");
|
||||
}
|
||||
|
||||
deepEqualCyclic.use = function (match) {
|
||||
return function deepEqual(a, b) {
|
||||
return deepEqualCyclic(a, b, match);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = deepEqualCyclic;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
var toString = require("@sinonjs/commons").prototypes.object.toString;
|
||||
|
||||
/**
|
||||
* Returns the internal `Class` by calling `Object.prototype.toString`
|
||||
* with the provided value as `this`. Return value is a `String`, naming the
|
||||
* internal class, e.g. "Array"
|
||||
*
|
||||
* @private
|
||||
* @param {*} value - Any value
|
||||
* @returns {string} - A string representation of the `Class` of `value`
|
||||
*/
|
||||
function getClass(value) {
|
||||
return toString(value).split(/[ \]]/)[1];
|
||||
}
|
||||
|
||||
module.exports = getClass;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
var isNaN = require("./is-nan");
|
||||
var isNegZero = require("./is-neg-zero");
|
||||
|
||||
/**
|
||||
* Strict equality check according to EcmaScript Harmony's `egal`.
|
||||
*
|
||||
* **From the Harmony wiki:**
|
||||
* > An `egal` function simply makes available the internal `SameValue` function
|
||||
* > from section 9.12 of the ES5 spec. If two values are egal, then they are not
|
||||
* > observably distinguishable.
|
||||
*
|
||||
* `identical` returns `true` when `===` is `true`, except for `-0` and
|
||||
* `+0`, where it returns `false`. Additionally, it returns `true` when
|
||||
* `NaN` is compared to itself.
|
||||
*
|
||||
* @alias module:samsam.identical
|
||||
* @param {*} obj1 The first value to compare
|
||||
* @param {*} obj2 The second value to compare
|
||||
* @returns {boolean} Returns `true` when the objects are *egal*, `false` otherwise
|
||||
*/
|
||||
function identical(obj1, obj2) {
|
||||
if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) {
|
||||
return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = identical;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
var getClass = require("./get-class");
|
||||
|
||||
/**
|
||||
* Returns `true` when `object` is an `arguments` object, `false` otherwise
|
||||
*
|
||||
* @alias module:samsam.isArguments
|
||||
* @param {*} object - The object to examine
|
||||
* @returns {boolean} `true` when `object` is an `arguments` object
|
||||
*/
|
||||
function isArguments(object) {
|
||||
return getClass(object) === "Arguments";
|
||||
}
|
||||
|
||||
module.exports = isArguments;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
var functionName = require("@sinonjs/commons").functionName;
|
||||
var indexOf = require("@sinonjs/commons").prototypes.array.indexOf;
|
||||
var map = require("@sinonjs/commons").prototypes.array.map;
|
||||
var ARRAY_TYPES = require("./array-types");
|
||||
var type = require("type-detect");
|
||||
|
||||
/**
|
||||
* Returns `true` when `object` is an array type, `false` otherwise
|
||||
*
|
||||
* @param {*} object - The object to examine
|
||||
* @returns {boolean} `true` when `object` is an array type
|
||||
* @private
|
||||
*/
|
||||
function isArrayType(object) {
|
||||
return indexOf(map(ARRAY_TYPES, functionName), type(object)) !== -1;
|
||||
}
|
||||
|
||||
module.exports = isArrayType;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when `value` is an instance of Date
|
||||
*
|
||||
* @private
|
||||
* @param {Date} value The value to examine
|
||||
* @returns {boolean} `true` when `value` is an instance of Date
|
||||
*/
|
||||
function isDate(value) {
|
||||
return value instanceof Date;
|
||||
}
|
||||
|
||||
module.exports = isDate;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
var div = typeof document !== "undefined" && document.createElement("div");
|
||||
|
||||
/**
|
||||
* Returns `true` when `object` is a DOM element node.
|
||||
*
|
||||
* Unlike Underscore.js/lodash, this function will return `false` if `object`
|
||||
* is an *element-like* object, i.e. a regular object with a `nodeType`
|
||||
* property that holds the value `1`.
|
||||
*
|
||||
* @alias module:samsam.isElement
|
||||
* @param {object} object The object to examine
|
||||
* @returns {boolean} Returns `true` for DOM element nodes
|
||||
*/
|
||||
function isElement(object) {
|
||||
if (!object || object.nodeType !== 1 || !div) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
object.appendChild(div);
|
||||
object.removeChild(div);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = isElement;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when the argument is an iterable, `false` otherwise
|
||||
*
|
||||
* @alias module:samsam.isIterable
|
||||
* @param {*} val - A value to examine
|
||||
* @returns {boolean} Returns `true` when the argument is an iterable, `false` otherwise
|
||||
*/
|
||||
function isIterable(val) {
|
||||
// checks for null and undefined
|
||||
if (typeof val !== "object") {
|
||||
return false;
|
||||
}
|
||||
return typeof val[Symbol.iterator] === "function";
|
||||
}
|
||||
|
||||
module.exports = isIterable;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when `value` is a Map
|
||||
*
|
||||
* @param {*} value A value to examine
|
||||
* @returns {boolean} `true` when `value` is an instance of `Map`, `false` otherwise
|
||||
* @private
|
||||
*/
|
||||
function isMap(value) {
|
||||
return typeof Map !== "undefined" && value instanceof Map;
|
||||
}
|
||||
|
||||
module.exports = isMap;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Compares a `value` to `NaN`
|
||||
*
|
||||
* @private
|
||||
* @param {*} value A value to examine
|
||||
* @returns {boolean} Returns `true` when `value` is `NaN`
|
||||
*/
|
||||
function isNaN(value) {
|
||||
// Unlike global `isNaN`, this function avoids type coercion
|
||||
// `typeof` check avoids IE host object issues, hat tip to
|
||||
// lodash
|
||||
|
||||
// eslint-disable-next-line no-self-compare
|
||||
return typeof value === "number" && value !== value;
|
||||
}
|
||||
|
||||
module.exports = isNaN;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when `value` is `-0`
|
||||
*
|
||||
* @alias module:samsam.isNegZero
|
||||
* @param {*} value A value to examine
|
||||
* @returns {boolean} Returns `true` when `value` is `-0`
|
||||
*/
|
||||
function isNegZero(value) {
|
||||
return value === 0 && 1 / value === -Infinity;
|
||||
}
|
||||
|
||||
module.exports = isNegZero;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when the value is a regular Object and not a specialized Object
|
||||
*
|
||||
* This helps speed up deepEqual cyclic checks
|
||||
*
|
||||
* The premise is that only Objects are stored in the visited array.
|
||||
* So if this function returns false, we don't have to do the
|
||||
* expensive operation of searching for the value in the the array of already
|
||||
* visited objects
|
||||
*
|
||||
* @private
|
||||
* @param {object} value The object to examine
|
||||
* @returns {boolean} `true` when the object is a non-specialised object
|
||||
*/
|
||||
function isObject(value) {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
// none of these are collection objects, so we can return false
|
||||
!(value instanceof Boolean) &&
|
||||
!(value instanceof Date) &&
|
||||
!(value instanceof Error) &&
|
||||
!(value instanceof Number) &&
|
||||
!(value instanceof RegExp) &&
|
||||
!(value instanceof String)
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = isObject;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns `true` when the argument is an instance of Set, `false` otherwise
|
||||
*
|
||||
* @alias module:samsam.isSet
|
||||
* @param {*} val - A value to examine
|
||||
* @returns {boolean} Returns `true` when the argument is an instance of Set, `false` otherwise
|
||||
*/
|
||||
function isSet(val) {
|
||||
return (typeof Set !== "undefined" && val instanceof Set) || false;
|
||||
}
|
||||
|
||||
module.exports = isSet;
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
var forEach = require("@sinonjs/commons").prototypes.set.forEach;
|
||||
|
||||
/**
|
||||
* Returns `true` when `s1` is a subset of `s2`, `false` otherwise
|
||||
*
|
||||
* @private
|
||||
* @param {Array|Set} s1 The target value
|
||||
* @param {Array|Set} s2 The containing value
|
||||
* @param {Function} compare A comparison function, should return `true` when
|
||||
* values are considered equal
|
||||
* @returns {boolean} Returns `true` when `s1` is a subset of `s2`, `false`` otherwise
|
||||
*/
|
||||
function isSubset(s1, s2, compare) {
|
||||
var allContained = true;
|
||||
forEach(s1, function (v1) {
|
||||
var includes = false;
|
||||
forEach(s2, function (v2) {
|
||||
if (compare(v2, v1)) {
|
||||
includes = true;
|
||||
}
|
||||
});
|
||||
allContained = allContained && includes;
|
||||
});
|
||||
|
||||
return allContained;
|
||||
}
|
||||
|
||||
module.exports = isSubset;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
var slice = require("@sinonjs/commons").prototypes.string.slice;
|
||||
var typeOf = require("@sinonjs/commons").typeOf;
|
||||
var valueToString = require("@sinonjs/commons").valueToString;
|
||||
|
||||
/**
|
||||
* Creates a string represenation of an iterable object
|
||||
*
|
||||
* @private
|
||||
* @param {object} obj The iterable object to stringify
|
||||
* @returns {string} A string representation
|
||||
*/
|
||||
function iterableToString(obj) {
|
||||
if (typeOf(obj) === "map") {
|
||||
return mapToString(obj);
|
||||
}
|
||||
|
||||
return genericIterableToString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of a Map
|
||||
*
|
||||
* @private
|
||||
* @param {Map} map The map to stringify
|
||||
* @returns {string} A string representation
|
||||
*/
|
||||
function mapToString(map) {
|
||||
var representation = "";
|
||||
|
||||
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
|
||||
map.forEach(function (value, key) {
|
||||
representation += `[${stringify(key)},${stringify(value)}],`;
|
||||
});
|
||||
|
||||
representation = slice(representation, 0, -1);
|
||||
return representation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string represenation for an iterable
|
||||
*
|
||||
* @private
|
||||
* @param {object} iterable The iterable to stringify
|
||||
* @returns {string} A string representation
|
||||
*/
|
||||
function genericIterableToString(iterable) {
|
||||
var representation = "";
|
||||
|
||||
// eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods
|
||||
iterable.forEach(function (value) {
|
||||
representation += `${stringify(value)},`;
|
||||
});
|
||||
|
||||
representation = slice(representation, 0, -1);
|
||||
return representation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the passed `item`
|
||||
*
|
||||
* @private
|
||||
* @param {object} item The item to stringify
|
||||
* @returns {string} A string representation of `item`
|
||||
*/
|
||||
function stringify(item) {
|
||||
return typeof item === "string" ? `'${item}'` : valueToString(item);
|
||||
}
|
||||
|
||||
module.exports = iterableToString;
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
"use strict";
|
||||
|
||||
var valueToString = require("@sinonjs/commons").valueToString;
|
||||
var indexOf = require("@sinonjs/commons").prototypes.string.indexOf;
|
||||
var forEach = require("@sinonjs/commons").prototypes.array.forEach;
|
||||
var type = require("type-detect");
|
||||
|
||||
var engineCanCompareMaps = typeof Array.from === "function";
|
||||
var deepEqual = require("./deep-equal").use(match); // eslint-disable-line no-use-before-define
|
||||
var isArrayType = require("./is-array-type");
|
||||
var isSubset = require("./is-subset");
|
||||
var createMatcher = require("./create-matcher");
|
||||
|
||||
/**
|
||||
* Returns true when `array` contains all of `subset` as defined by the `compare`
|
||||
* argument
|
||||
*
|
||||
* @param {Array} array An array to search for a subset
|
||||
* @param {Array} subset The subset to find in the array
|
||||
* @param {Function} compare A comparison function
|
||||
* @returns {boolean} [description]
|
||||
* @private
|
||||
*/
|
||||
function arrayContains(array, subset, compare) {
|
||||
if (subset.length === 0) {
|
||||
return true;
|
||||
}
|
||||
var i, l, j, k;
|
||||
for (i = 0, l = array.length; i < l; ++i) {
|
||||
if (compare(array[i], subset[0])) {
|
||||
for (j = 0, k = subset.length; j < k; ++j) {
|
||||
if (i + j >= l) {
|
||||
return false;
|
||||
}
|
||||
if (!compare(array[i + j], subset[j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* eslint-disable complexity */
|
||||
/**
|
||||
* Matches an object with a matcher (or value)
|
||||
*
|
||||
* @alias module:samsam.match
|
||||
* @param {object} object The object candidate to match
|
||||
* @param {object} matcherOrValue A matcher or value to match against
|
||||
* @returns {boolean} true when `object` matches `matcherOrValue`
|
||||
*/
|
||||
function match(object, matcherOrValue) {
|
||||
if (matcherOrValue && typeof matcherOrValue.test === "function") {
|
||||
return matcherOrValue.test(object);
|
||||
}
|
||||
|
||||
switch (type(matcherOrValue)) {
|
||||
case "bigint":
|
||||
case "boolean":
|
||||
case "number":
|
||||
case "symbol":
|
||||
return matcherOrValue === object;
|
||||
case "function":
|
||||
return matcherOrValue(object) === true;
|
||||
case "string":
|
||||
var notNull = typeof object === "string" || Boolean(object);
|
||||
return (
|
||||
notNull &&
|
||||
indexOf(
|
||||
valueToString(object).toLowerCase(),
|
||||
matcherOrValue.toLowerCase(),
|
||||
) >= 0
|
||||
);
|
||||
case "null":
|
||||
return object === null;
|
||||
case "undefined":
|
||||
return typeof object === "undefined";
|
||||
case "Date":
|
||||
/* istanbul ignore else */
|
||||
if (type(object) === "Date") {
|
||||
return object.getTime() === matcherOrValue.getTime();
|
||||
}
|
||||
/* istanbul ignore next: this is basically the rest of the function, which is covered */
|
||||
break;
|
||||
case "Array":
|
||||
case "Int8Array":
|
||||
case "Uint8Array":
|
||||
case "Uint8ClampedArray":
|
||||
case "Int16Array":
|
||||
case "Uint16Array":
|
||||
case "Int32Array":
|
||||
case "Uint32Array":
|
||||
case "Float32Array":
|
||||
case "Float64Array":
|
||||
return (
|
||||
isArrayType(matcherOrValue) &&
|
||||
arrayContains(object, matcherOrValue, match)
|
||||
);
|
||||
case "Map":
|
||||
/* istanbul ignore next: this is covered by a test, that is only run in IE, but we collect coverage information in node*/
|
||||
if (!engineCanCompareMaps) {
|
||||
throw new Error(
|
||||
"The JavaScript engine does not support Array.from and cannot reliably do value comparison of Map instances",
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
type(object) === "Map" &&
|
||||
arrayContains(
|
||||
Array.from(object),
|
||||
Array.from(matcherOrValue),
|
||||
match,
|
||||
)
|
||||
);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type(object)) {
|
||||
case "null":
|
||||
return false;
|
||||
case "Set":
|
||||
return isSubset(matcherOrValue, object, match);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (matcherOrValue && typeof matcherOrValue === "object") {
|
||||
if (matcherOrValue === object) {
|
||||
return true;
|
||||
}
|
||||
if (typeof object !== "object") {
|
||||
return false;
|
||||
}
|
||||
var prop;
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (prop in matcherOrValue) {
|
||||
var value = object[prop];
|
||||
if (
|
||||
typeof value === "undefined" &&
|
||||
typeof object.getAttribute === "function"
|
||||
) {
|
||||
value = object.getAttribute(prop);
|
||||
}
|
||||
if (
|
||||
matcherOrValue[prop] === null ||
|
||||
typeof matcherOrValue[prop] === "undefined"
|
||||
) {
|
||||
if (value !== matcherOrValue[prop]) {
|
||||
return false;
|
||||
}
|
||||
} else if (
|
||||
typeof value === "undefined" ||
|
||||
!deepEqual(value, matcherOrValue[prop])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
throw new Error("Matcher was an unknown or unsupported type");
|
||||
}
|
||||
/* eslint-enable complexity */
|
||||
|
||||
forEach(Object.keys(createMatcher), function (key) {
|
||||
match[key] = createMatcher[key];
|
||||
});
|
||||
|
||||
module.exports = match;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @module samsam
|
||||
*/
|
||||
var identical = require("./identical");
|
||||
var isArguments = require("./is-arguments");
|
||||
var isElement = require("./is-element");
|
||||
var isNegZero = require("./is-neg-zero");
|
||||
var isSet = require("./is-set");
|
||||
var isMap = require("./is-map");
|
||||
var match = require("./match");
|
||||
var deepEqualCyclic = require("./deep-equal").use(match);
|
||||
var createMatcher = require("./create-matcher");
|
||||
|
||||
module.exports = {
|
||||
createMatcher: createMatcher,
|
||||
deepEqual: deepEqualCyclic,
|
||||
identical: identical,
|
||||
isArguments: isArguments,
|
||||
isElement: isElement,
|
||||
isMap: isMap,
|
||||
isNegZero: isNegZero,
|
||||
isSet: isSet,
|
||||
match: match,
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Jake Luer <jake@alogicalparadox.com> (http://alogicalparadox.com)
|
||||
|
||||
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.
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
<h1 align=center>
|
||||
<a href="http://chaijs.com" title="Chai Documentation">
|
||||
<img alt="type-detect" src="https://raw.githubusercontent.com/chaijs/type-detect/master/type-detect-logo.svg"/>
|
||||
</a>
|
||||
</h1>
|
||||
<br>
|
||||
<p align=center>
|
||||
Improved typeof detection for <a href="https://nodejs.org">node</a>, <a href="https://deno.land/">Deno</a>, and the browser.
|
||||
</p>
|
||||
|
||||
<p align=center>
|
||||
<a href="./LICENSE">
|
||||
<img
|
||||
alt="license:mit"
|
||||
src="https://img.shields.io/badge/license-mit-green.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/packages/type-detect">
|
||||
<img
|
||||
alt="npm:?"
|
||||
src="https://img.shields.io/npm/v/type-detect.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://github.com/chaijs/type-detect">
|
||||
<img
|
||||
alt="build:?"
|
||||
src="https://github.com/chaijs/type-detect/workflows/Build/badge.svg"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://coveralls.io/r/chaijs/type-detect">
|
||||
<img
|
||||
alt="coverage:?"
|
||||
src="https://img.shields.io/coveralls/chaijs/type-detect/master.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/packages/type-detect">
|
||||
<img
|
||||
alt="dependencies:?"
|
||||
src="https://img.shields.io/npm/dm/type-detect.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<a href="">
|
||||
<img
|
||||
alt="devDependencies:?"
|
||||
src="https://img.shields.io/david/chaijs/type-detect.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://chai-slack.herokuapp.com/">
|
||||
<img
|
||||
alt="Join the Slack chat"
|
||||
src="https://img.shields.io/badge/slack-join%20chat-E2206F.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://gitter.im/chaijs/chai">
|
||||
<img
|
||||
alt="Join the Gitter chat"
|
||||
src="https://img.shields.io/badge/gitter-join%20chat-D0104D.svg?style=flat-square"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
<div align=center>
|
||||
<table width="100%">
|
||||
<tr><th colspan=6>Supported Browsers</th></tr> <tr>
|
||||
<th align=center><img src="https://camo.githubusercontent.com/ab586f11dfcb49bf5f2c2fa9adadc5e857de122a/687474703a2f2f73766773686172652e636f6d2f692f3278532e737667" alt=""> Chrome</th>
|
||||
<th align=center><img src="https://camo.githubusercontent.com/98cca3108c18dcfaa62667b42046540c6822cdac/687474703a2f2f73766773686172652e636f6d2f692f3279352e737667" alt=""> Edge</th>
|
||||
<th align=center><img src="https://camo.githubusercontent.com/acdcb09840a9e1442cbaf1b684f95ab3c3f41cf4/687474703a2f2f73766773686172652e636f6d2f692f3279462e737667" alt=""> Firefox</th>
|
||||
<th align=center><img src="https://camo.githubusercontent.com/728f8cb0bee9ed58ab85e39266f1152c53e0dffd/687474703a2f2f73766773686172652e636f6d2f692f3278342e737667" alt=""> Safari</th>
|
||||
<th align=center><img src="https://camo.githubusercontent.com/96a2317034dee0040d0a762e7a30c3c650c45aac/687474703a2f2f73766773686172652e636f6d2f692f3279532e737667" alt=""> IE</th>
|
||||
</tr><tr>
|
||||
<td align=center>✅</td>
|
||||
<td align=center>✅</td>
|
||||
<td align=center>✅</td>
|
||||
<td align=center>✅</td>
|
||||
<td align=center>9, 10, 11</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
## What is Type-Detect?
|
||||
|
||||
Type Detect is a module which you can use to detect the type of a given object. It returns a string representation of the object's type, either using [`typeof`](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-typeof-operator) or [`@@toStringTag`](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-symbol.tostringtag). It also normalizes some object names for consistency among browsers.
|
||||
|
||||
## Why?
|
||||
|
||||
The `typeof` operator will only specify primitive values; everything else is `"object"` (including `null`, arrays, regexps, etc). Many developers use `Object.prototype.toString()` - which is a fine alternative and returns many more types (null returns `[object Null]`, Arrays as `[object Array]`, regexps as `[object RegExp]` etc).
|
||||
|
||||
Sadly, `Object.prototype.toString` is slow, and buggy. By slow - we mean it is slower than `typeof`. By buggy - we mean that some values (like Promises, the global object, iterators, dataviews, a bunch of HTML elements) all report different things in different browsers.
|
||||
|
||||
`type-detect` fixes all of the shortcomings with `Object.prototype.toString`. We have extra code to speed up checks of JS and DOM objects, as much as 20-30x faster for some values. `type-detect` also fixes any consistencies with these objects.
|
||||
|
||||
## Installation
|
||||
|
||||
### Node.js
|
||||
|
||||
`type-detect` is available on [npm](http://npmjs.org). To install it, type:
|
||||
|
||||
$ npm install type-detect
|
||||
|
||||
### Deno
|
||||
|
||||
`type-detect` can be imported with the following line:
|
||||
|
||||
```js
|
||||
import type from 'https://deno.land/x/type_detect@v4.1.0/index.ts'
|
||||
```
|
||||
|
||||
### Browsers
|
||||
|
||||
You can also use it within the browser; install via npm and use the `type-detect.js` file found within the download. For example:
|
||||
|
||||
```html
|
||||
<script src="./node_modules/type-detect/type-detect.js"></script>
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The primary export of `type-detect` is function that can serve as a replacement for `typeof`. The results of this function will be more specific than that of native `typeof`.
|
||||
|
||||
```js
|
||||
var type = require('type-detect');
|
||||
```
|
||||
Or, in the browser use case, after the <script> tag,
|
||||
```js
|
||||
var type = typeDetect;
|
||||
```
|
||||
|
||||
#### array
|
||||
|
||||
```js
|
||||
assert(type([]) === 'Array');
|
||||
assert(type(new Array()) === 'Array');
|
||||
```
|
||||
|
||||
#### regexp
|
||||
|
||||
```js
|
||||
assert(type(/a-z/gi) === 'RegExp');
|
||||
assert(type(new RegExp('a-z')) === 'RegExp');
|
||||
```
|
||||
|
||||
#### function
|
||||
|
||||
```js
|
||||
assert(type(function () {}) === 'function');
|
||||
```
|
||||
|
||||
#### arguments
|
||||
|
||||
```js
|
||||
(function () {
|
||||
assert(type(arguments) === 'Arguments');
|
||||
})();
|
||||
```
|
||||
|
||||
#### date
|
||||
|
||||
```js
|
||||
assert(type(new Date) === 'Date');
|
||||
```
|
||||
|
||||
#### number
|
||||
|
||||
```js
|
||||
assert(type(1) === 'number');
|
||||
assert(type(1.234) === 'number');
|
||||
assert(type(-1) === 'number');
|
||||
assert(type(-1.234) === 'number');
|
||||
assert(type(Infinity) === 'number');
|
||||
assert(type(NaN) === 'number');
|
||||
assert(type(new Number(1)) === 'Number'); // note - the object version has a capital N
|
||||
```
|
||||
|
||||
#### string
|
||||
|
||||
```js
|
||||
assert(type('hello world') === 'string');
|
||||
assert(type(new String('hello')) === 'String'); // note - the object version has a capital S
|
||||
```
|
||||
|
||||
#### null
|
||||
|
||||
```js
|
||||
assert(type(null) === 'null');
|
||||
assert(type(undefined) !== 'null');
|
||||
```
|
||||
|
||||
#### undefined
|
||||
|
||||
```js
|
||||
assert(type(undefined) === 'undefined');
|
||||
assert(type(null) !== 'undefined');
|
||||
```
|
||||
|
||||
#### object
|
||||
|
||||
```js
|
||||
var Noop = function () {};
|
||||
assert(type({}) === 'Object');
|
||||
assert(type(Noop) !== 'Object');
|
||||
assert(type(new Noop) === 'Object');
|
||||
assert(type(new Object) === 'Object');
|
||||
```
|
||||
|
||||
#### ECMA6 Types
|
||||
|
||||
All new ECMAScript 2015 objects are also supported, such as Promises and Symbols:
|
||||
|
||||
```js
|
||||
assert(type(new Map() === 'Map');
|
||||
assert(type(new WeakMap()) === 'WeakMap');
|
||||
assert(type(new Set()) === 'Set');
|
||||
assert(type(new WeakSet()) === 'WeakSet');
|
||||
assert(type(Symbol()) === 'symbol');
|
||||
assert(type(new Promise(callback) === 'Promise');
|
||||
assert(type(new Int8Array()) === 'Int8Array');
|
||||
assert(type(new Uint8Array()) === 'Uint8Array');
|
||||
assert(type(new UInt8ClampedArray()) === 'Uint8ClampedArray');
|
||||
assert(type(new Int16Array()) === 'Int16Array');
|
||||
assert(type(new Uint16Array()) === 'Uint16Array');
|
||||
assert(type(new Int32Array()) === 'Int32Array');
|
||||
assert(type(new UInt32Array()) === 'Uint32Array');
|
||||
assert(type(new Float32Array()) === 'Float32Array');
|
||||
assert(type(new Float64Array()) === 'Float64Array');
|
||||
assert(type(new ArrayBuffer()) === 'ArrayBuffer');
|
||||
assert(type(new DataView(arrayBuffer)) === 'DataView');
|
||||
```
|
||||
|
||||
Also, if you use `Symbol.toStringTag` to change an Objects return value of the `toString()` Method, `type()` will return this value, e.g:
|
||||
|
||||
```js
|
||||
var myObject = {};
|
||||
myObject[Symbol.toStringTag] = 'myCustomType';
|
||||
assert(type(myObject) === 'myCustomType');
|
||||
```
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
const promiseExists = typeof Promise === 'function';
|
||||
const globalObject = ((Obj) => {
|
||||
if (typeof globalThis === 'object') {
|
||||
return globalThis;
|
||||
}
|
||||
Object.defineProperty(Obj, 'typeDetectGlobalObject', {
|
||||
get() {
|
||||
return this;
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
const global = typeDetectGlobalObject;
|
||||
delete Obj.typeDetectGlobalObject;
|
||||
return global;
|
||||
})(Object.prototype);
|
||||
const symbolExists = typeof Symbol !== 'undefined';
|
||||
const mapExists = typeof Map !== 'undefined';
|
||||
const setExists = typeof Set !== 'undefined';
|
||||
const weakMapExists = typeof WeakMap !== 'undefined';
|
||||
const weakSetExists = typeof WeakSet !== 'undefined';
|
||||
const dataViewExists = typeof DataView !== 'undefined';
|
||||
const symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined';
|
||||
const symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined';
|
||||
const setEntriesExists = setExists && typeof Set.prototype.entries === 'function';
|
||||
const mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function';
|
||||
const setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries());
|
||||
const mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries());
|
||||
const arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function';
|
||||
const arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]());
|
||||
const stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function';
|
||||
const stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]());
|
||||
const toStringLeftSliceLength = 8;
|
||||
const toStringRightSliceLength = -1;
|
||||
export default function typeDetect(obj) {
|
||||
const typeofObj = typeof obj;
|
||||
if (typeofObj !== 'object') {
|
||||
return typeofObj;
|
||||
}
|
||||
if (obj === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (obj === globalObject) {
|
||||
return 'global';
|
||||
}
|
||||
if (Array.isArray(obj) &&
|
||||
(symbolToStringTagExists === false || !(Symbol.toStringTag in obj))) {
|
||||
return 'Array';
|
||||
}
|
||||
if (typeof window === 'object' && window !== null) {
|
||||
if (typeof window.location === 'object' && obj === window.location) {
|
||||
return 'Location';
|
||||
}
|
||||
if (typeof window.document === 'object' && obj === window.document) {
|
||||
return 'Document';
|
||||
}
|
||||
if (typeof window.navigator === 'object') {
|
||||
if (typeof window.navigator.mimeTypes === 'object' &&
|
||||
obj === window.navigator.mimeTypes) {
|
||||
return 'MimeTypeArray';
|
||||
}
|
||||
if (typeof window.navigator.plugins === 'object' &&
|
||||
obj === window.navigator.plugins) {
|
||||
return 'PluginArray';
|
||||
}
|
||||
}
|
||||
if ((typeof window.HTMLElement === 'function' ||
|
||||
typeof window.HTMLElement === 'object') &&
|
||||
obj instanceof window.HTMLElement) {
|
||||
if (obj.tagName === 'BLOCKQUOTE') {
|
||||
return 'HTMLQuoteElement';
|
||||
}
|
||||
if (obj.tagName === 'TD') {
|
||||
return 'HTMLTableDataCellElement';
|
||||
}
|
||||
if (obj.tagName === 'TH') {
|
||||
return 'HTMLTableHeaderCellElement';
|
||||
}
|
||||
}
|
||||
}
|
||||
const stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]);
|
||||
if (typeof stringTag === 'string') {
|
||||
return stringTag;
|
||||
}
|
||||
const objPrototype = Object.getPrototypeOf(obj);
|
||||
if (objPrototype === RegExp.prototype) {
|
||||
return 'RegExp';
|
||||
}
|
||||
if (objPrototype === Date.prototype) {
|
||||
return 'Date';
|
||||
}
|
||||
if (promiseExists && objPrototype === Promise.prototype) {
|
||||
return 'Promise';
|
||||
}
|
||||
if (setExists && objPrototype === Set.prototype) {
|
||||
return 'Set';
|
||||
}
|
||||
if (mapExists && objPrototype === Map.prototype) {
|
||||
return 'Map';
|
||||
}
|
||||
if (weakSetExists && objPrototype === WeakSet.prototype) {
|
||||
return 'WeakSet';
|
||||
}
|
||||
if (weakMapExists && objPrototype === WeakMap.prototype) {
|
||||
return 'WeakMap';
|
||||
}
|
||||
if (dataViewExists && objPrototype === DataView.prototype) {
|
||||
return 'DataView';
|
||||
}
|
||||
if (mapExists && objPrototype === mapIteratorPrototype) {
|
||||
return 'Map Iterator';
|
||||
}
|
||||
if (setExists && objPrototype === setIteratorPrototype) {
|
||||
return 'Set Iterator';
|
||||
}
|
||||
if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) {
|
||||
return 'Array Iterator';
|
||||
}
|
||||
if (stringIteratorExists && objPrototype === stringIteratorPrototype) {
|
||||
return 'String Iterator';
|
||||
}
|
||||
if (objPrototype === null) {
|
||||
return 'Object';
|
||||
}
|
||||
return Object
|
||||
.prototype
|
||||
.toString
|
||||
.call(obj)
|
||||
.slice(toStringLeftSliceLength, toStringRightSliceLength);
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"name": "type-detect",
|
||||
"version": "4.1.0",
|
||||
"description": "Improved typeof detection for node.js and the browser.",
|
||||
"keywords": [
|
||||
"type",
|
||||
"typeof",
|
||||
"types"
|
||||
],
|
||||
"license": "MIT",
|
||||
"author": "Jake Luer <jake@alogicalparadox.com> (http://alogicalparadox.com)",
|
||||
"contributors": [
|
||||
"Keith Cirkel (https://github.com/keithamus)",
|
||||
"David Losert (https://github.com/davelosert)",
|
||||
"Aleksey Shvayka (https://github.com/shvaikalesh)",
|
||||
"Lucas Fernandes da Costa (https://github.com/lucasfcosta)",
|
||||
"Grant Snodgrass (https://github.com/meeber)",
|
||||
"Jeremy Tice (https://github.com/jetpacmonkey)",
|
||||
"Edward Betts (https://github.com/EdwardBetts)",
|
||||
"dvlsg (https://github.com/dvlsg)",
|
||||
"Amila Welihinda (https://github.com/amilajack)",
|
||||
"Jake Champion (https://github.com/JakeChampion)",
|
||||
"Miroslav Bajtoš (https://github.com/bajtos)"
|
||||
],
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.ts",
|
||||
"index.d.ts",
|
||||
"type-detect.js"
|
||||
],
|
||||
"main": "./type-detect.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/chaijs/type-detect.git"
|
||||
},
|
||||
"scripts": {
|
||||
"bench": "node bench",
|
||||
"build": "tsc && rollup -c rollup.conf.js",
|
||||
"commit-msg": "commitlint -x angular",
|
||||
"lint": "eslint --ignore-path .gitignore . --ext .js,.ts",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
|
||||
"pretest:node": "cross-env NODE_ENV=test npm run build",
|
||||
"pretest:browser": "cross-env NODE_ENV=test npm run build",
|
||||
"test": "npm run test:node && npm run test:browser",
|
||||
"test:browser": "karma start --singleRun=true",
|
||||
"test:node": "nyc mocha type-detect.test.js",
|
||||
"test:deno": "deno test test/deno-test.ts",
|
||||
"posttest:node": "nyc report --report-dir \"coverage/node-$(node --version)\" --reporter=lcovonly && npm run upload-coverage",
|
||||
"posttest:browser": "npm run upload-coverage",
|
||||
"upload-coverage": "codecov"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": [
|
||||
"strict/es6"
|
||||
],
|
||||
"globals": {
|
||||
"HTMLElement": false,
|
||||
"window": false
|
||||
},
|
||||
"rules": {
|
||||
"complexity": 0,
|
||||
"max-statements": 0,
|
||||
"prefer-rest-params": 0
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^13.1.0",
|
||||
"@rollup/plugin-buble": "^0.21.3",
|
||||
"@rollup/plugin-commonjs": "^20.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
||||
"@typescript-eslint/parser": "^4.31.2",
|
||||
"benchmark": "^2.1.4",
|
||||
"buble": "^0.20.0",
|
||||
"codecov": "^3.8.3",
|
||||
"commitlint-config-angular": "^13.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-strict": "^14.0.1",
|
||||
"eslint-plugin-filenames": "^1.3.2",
|
||||
"husky": "^7.0.2",
|
||||
"karma": "^6.3.4",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage": "^2.0.3",
|
||||
"karma-detect-browsers": "^2.3.3",
|
||||
"karma-edge-launcher": "^0.4.2",
|
||||
"karma-firefox-launcher": "^2.1.1",
|
||||
"karma-ie-launcher": "^1.0.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-opera-launcher": "^1.0.0",
|
||||
"karma-safari-launcher": "^1.0.0",
|
||||
"karma-safaritechpreview-launcher": "^2.0.2",
|
||||
"karma-sauce-launcher": "^4.3.6",
|
||||
"mocha": "^9.1.1",
|
||||
"nyc": "^15.1.0",
|
||||
"rollup": "^2.57.0",
|
||||
"rollup-plugin-istanbul": "^3.0.0",
|
||||
"semantic-release": "^18.0.0",
|
||||
"simple-assert": "^1.0.0",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.typeDetect = factory());
|
||||
})(this, (function () { 'use strict';
|
||||
|
||||
var promiseExists = typeof Promise === 'function';
|
||||
var globalObject = (function (Obj) {
|
||||
if (typeof globalThis === 'object') {
|
||||
return globalThis;
|
||||
}
|
||||
Object.defineProperty(Obj, 'typeDetectGlobalObject', {
|
||||
get: function get() {
|
||||
return this;
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
var global = typeDetectGlobalObject;
|
||||
delete Obj.typeDetectGlobalObject;
|
||||
return global;
|
||||
})(Object.prototype);
|
||||
var symbolExists = typeof Symbol !== 'undefined';
|
||||
var mapExists = typeof Map !== 'undefined';
|
||||
var setExists = typeof Set !== 'undefined';
|
||||
var weakMapExists = typeof WeakMap !== 'undefined';
|
||||
var weakSetExists = typeof WeakSet !== 'undefined';
|
||||
var dataViewExists = typeof DataView !== 'undefined';
|
||||
var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined';
|
||||
var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined';
|
||||
var setEntriesExists = setExists && typeof Set.prototype.entries === 'function';
|
||||
var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function';
|
||||
var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries());
|
||||
var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries());
|
||||
var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function';
|
||||
var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]());
|
||||
var stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function';
|
||||
var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]());
|
||||
var toStringLeftSliceLength = 8;
|
||||
var toStringRightSliceLength = -1;
|
||||
function typeDetect(obj) {
|
||||
var typeofObj = typeof obj;
|
||||
if (typeofObj !== 'object') {
|
||||
return typeofObj;
|
||||
}
|
||||
if (obj === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (obj === globalObject) {
|
||||
return 'global';
|
||||
}
|
||||
if (Array.isArray(obj) &&
|
||||
(symbolToStringTagExists === false || !(Symbol.toStringTag in obj))) {
|
||||
return 'Array';
|
||||
}
|
||||
if (typeof window === 'object' && window !== null) {
|
||||
if (typeof window.location === 'object' && obj === window.location) {
|
||||
return 'Location';
|
||||
}
|
||||
if (typeof window.document === 'object' && obj === window.document) {
|
||||
return 'Document';
|
||||
}
|
||||
if (typeof window.navigator === 'object') {
|
||||
if (typeof window.navigator.mimeTypes === 'object' &&
|
||||
obj === window.navigator.mimeTypes) {
|
||||
return 'MimeTypeArray';
|
||||
}
|
||||
if (typeof window.navigator.plugins === 'object' &&
|
||||
obj === window.navigator.plugins) {
|
||||
return 'PluginArray';
|
||||
}
|
||||
}
|
||||
if ((typeof window.HTMLElement === 'function' ||
|
||||
typeof window.HTMLElement === 'object') &&
|
||||
obj instanceof window.HTMLElement) {
|
||||
if (obj.tagName === 'BLOCKQUOTE') {
|
||||
return 'HTMLQuoteElement';
|
||||
}
|
||||
if (obj.tagName === 'TD') {
|
||||
return 'HTMLTableDataCellElement';
|
||||
}
|
||||
if (obj.tagName === 'TH') {
|
||||
return 'HTMLTableHeaderCellElement';
|
||||
}
|
||||
}
|
||||
}
|
||||
var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]);
|
||||
if (typeof stringTag === 'string') {
|
||||
return stringTag;
|
||||
}
|
||||
var objPrototype = Object.getPrototypeOf(obj);
|
||||
if (objPrototype === RegExp.prototype) {
|
||||
return 'RegExp';
|
||||
}
|
||||
if (objPrototype === Date.prototype) {
|
||||
return 'Date';
|
||||
}
|
||||
if (promiseExists && objPrototype === Promise.prototype) {
|
||||
return 'Promise';
|
||||
}
|
||||
if (setExists && objPrototype === Set.prototype) {
|
||||
return 'Set';
|
||||
}
|
||||
if (mapExists && objPrototype === Map.prototype) {
|
||||
return 'Map';
|
||||
}
|
||||
if (weakSetExists && objPrototype === WeakSet.prototype) {
|
||||
return 'WeakSet';
|
||||
}
|
||||
if (weakMapExists && objPrototype === WeakMap.prototype) {
|
||||
return 'WeakMap';
|
||||
}
|
||||
if (dataViewExists && objPrototype === DataView.prototype) {
|
||||
return 'DataView';
|
||||
}
|
||||
if (mapExists && objPrototype === mapIteratorPrototype) {
|
||||
return 'Map Iterator';
|
||||
}
|
||||
if (setExists && objPrototype === setIteratorPrototype) {
|
||||
return 'Set Iterator';
|
||||
}
|
||||
if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) {
|
||||
return 'Array Iterator';
|
||||
}
|
||||
if (stringIteratorExists && objPrototype === stringIteratorPrototype) {
|
||||
return 'String Iterator';
|
||||
}
|
||||
if (objPrototype === null) {
|
||||
return 'Object';
|
||||
}
|
||||
return Object
|
||||
.prototype
|
||||
.toString
|
||||
.call(obj)
|
||||
.slice(toStringLeftSliceLength, toStringRightSliceLength);
|
||||
}
|
||||
|
||||
return typeDetect;
|
||||
|
||||
}));
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "@sinonjs/samsam",
|
||||
"version": "8.0.2",
|
||||
"description": "Value identification and comparison functions",
|
||||
"homepage": "http://sinonjs.github.io/samsam/",
|
||||
"author": "Christian Johansen",
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "./lib/samsam",
|
||||
"types": "./types/samsam.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sinonjs/samsam.git"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,css,md}": "prettier --check",
|
||||
"*.js": "eslint"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "node lib/deep-equal-benchmark.js",
|
||||
"build": "rm -rf types && tsc",
|
||||
"jsdoc": "jsdoc -c jsdoc.conf.json",
|
||||
"lint": "eslint .",
|
||||
"prepublishOnly": "npm run build && mkdocs gh-deploy -r upstream || mkdocs gh-deploy -r origin",
|
||||
"test": "mocha ./lib/*.test.js",
|
||||
"test-cloud": "npm run test-headless -- --wd",
|
||||
"test-check-coverage": "npm run test-coverage && nyc check-coverage --branches 100 --functions 100 --lines 100",
|
||||
"test-coverage": "nyc --all --reporter text --reporter html --reporter lcovonly npm run test",
|
||||
"test-headless": "mochify --no-detect-globals --recursive -R dot --plugin [ proxyquire-universal ] \"./lib/*.test.js\"",
|
||||
"prettier:check": "prettier --check '**/*.{js,css,md}'",
|
||||
"prettier:write": "prettier --write '**/*.{js,css,md}'",
|
||||
"preversion": "./check-external-dependencies.sh && npm run test-check-coverage",
|
||||
"version": "changes --commits --footer",
|
||||
"postversion": "git push --follow-tags && npm publish --access public",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"browser": {
|
||||
"jsdom": false,
|
||||
"jsdom-global": false
|
||||
},
|
||||
"files": [
|
||||
"docs/",
|
||||
"lib/",
|
||||
"!lib/**/*.test.js",
|
||||
"types/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"type-detect": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sinonjs/eslint-config": "^5.0.3",
|
||||
"@sinonjs/eslint-plugin-no-prototype-methods": "^0.1.1",
|
||||
"@sinonjs/referee": "^11.0.1",
|
||||
"@studio/changes": "^3.0.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"husky": "^9.1.6",
|
||||
"jquery": "^3.7.1",
|
||||
"jsdoc": "^4.0.3",
|
||||
"jsdom": "^25.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"lint-staged": "^15.2.10",
|
||||
"microtime": "^3.1.1",
|
||||
"mocha": "^10.7.3",
|
||||
"mochify": "^9.2.0",
|
||||
"nyc": "^17.0.0",
|
||||
"prettier": "^3.3.3",
|
||||
"proxyquire": "^2.1.3",
|
||||
"proxyquire-universal": "^3.0.1",
|
||||
"proxyquireify": "^3.2.1",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"nyc": {
|
||||
"exclude": [
|
||||
"**/*.test.js",
|
||||
"coverage/**",
|
||||
"dist/**",
|
||||
"out/**",
|
||||
"site/**",
|
||||
"eslint-local-rules.js",
|
||||
"rollup.config.js",
|
||||
"lib/deep-equal-benchmark.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
The encoding indexes, algorithms, and many comments in the code
|
||||
derive from the Encoding Standard https://encoding.spec.whatwg.org/
|
||||
|
||||
Otherwise, the code of this repository is released under the Unlicense
|
||||
license and is also dual-licensed under an Apache 2.0 license. Both
|
||||
are included below.
|
||||
|
||||
# Unlicense
|
||||
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
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 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
# Apache 2.0 License
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
text-encoding
|
||||
==============
|
||||
|
||||
This is a fork of https://github.com/inexorabletash/text-encoding, which
|
||||
has been marked as deprecated in the npm registry.
|
||||
|
||||
```
|
||||
npm info text-encoding
|
||||
|
||||
text-encoding@0.7.0 | (Unlicense OR Apache-2.0) | deps: none | versions: 11
|
||||
Polyfill for the Encoding Living Standard's API.
|
||||
https://github.com/inexorabletash/text-encoding
|
||||
|
||||
DEPRECATED ⚠️ - no longer maintained
|
||||
|
||||
keywords: encoding, decoding, living standard
|
||||
|
||||
dist
|
||||
.tarball: https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz
|
||||
.shasum: f895e836e45990624086601798ea98e8f36ee643
|
||||
.integrity: sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==
|
||||
.unpackedSize: 649.6 kB
|
||||
|
||||
maintainers:
|
||||
- inexorabletash <inexorabletash@gmail.com>
|
||||
|
||||
dist-tags:
|
||||
latest: 0.7.0
|
||||
|
||||
published a year ago by inexorabletash <inexorabletash@gmail.com>
|
||||
```
|
||||
|
||||
This fork is published as `@sinonjs/text-encoding` and will be available as long
|
||||
as it is in use by `@sinonjs` packages.
|
||||
|
||||
<hr />
|
||||
|
||||
This is a polyfill for the [Encoding Living
|
||||
Standard](https://encoding.spec.whatwg.org/) API for the Web, allowing
|
||||
encoding and decoding of textual data to and from Typed Array buffers
|
||||
for binary data in JavaScript.
|
||||
|
||||
By default it adheres to the spec and does not support *encoding* to
|
||||
legacy encodings, only *decoding*. It is also implemented to match the
|
||||
specification's algorithms, rather than for performance. The intended
|
||||
use is within Web pages, so it has no dependency on server frameworks
|
||||
or particular module schemes.
|
||||
|
||||
Basic examples and tests are included.
|
||||
|
||||
### Install ###
|
||||
|
||||
There are a few ways you can get and use the `@sinonjs/text-encoding` library.
|
||||
|
||||
### HTML Page Usage ###
|
||||
|
||||
Clone the repo and include the files directly:
|
||||
|
||||
```html
|
||||
<!-- Required for non-UTF encodings -->
|
||||
<script src="encoding-indexes.js"></script>
|
||||
<script src="encoding.js"></script>
|
||||
```
|
||||
|
||||
This is the only use case the developer cares about. If you want those
|
||||
fancy module and/or package manager things that are popular these days
|
||||
you should probably use a different library.
|
||||
|
||||
#### Package Managers ####
|
||||
|
||||
The package is published to **npm** as `@sinonjs/text-encoding`.
|
||||
Use through these is not really supported, since they aren't used by
|
||||
the developer of the library. Using `require()` in interesting ways
|
||||
probably breaks. Patches welcome, as long as they don't break the
|
||||
basic use of the files via `<script>`.
|
||||
|
||||
### API Overview ###
|
||||
|
||||
Basic Usage
|
||||
|
||||
```js
|
||||
var uint8array = new TextEncoder().encode(string);
|
||||
var string = new TextDecoder(encoding).decode(uint8array);
|
||||
```
|
||||
|
||||
Streaming Decode
|
||||
|
||||
```js
|
||||
var string = "", decoder = new TextDecoder(encoding), buffer;
|
||||
while (buffer = next_chunk()) {
|
||||
string += decoder.decode(buffer, {stream:true});
|
||||
}
|
||||
string += decoder.decode(); // finish the stream
|
||||
```
|
||||
|
||||
### Encodings ###
|
||||
|
||||
All encodings from the Encoding specification are supported:
|
||||
|
||||
utf-8 ibm866 iso-8859-2 iso-8859-3 iso-8859-4 iso-8859-5 iso-8859-6
|
||||
iso-8859-7 iso-8859-8 iso-8859-8-i iso-8859-10 iso-8859-13 iso-8859-14
|
||||
iso-8859-15 iso-8859-16 koi8-r koi8-u macintosh windows-874
|
||||
windows-1250 windows-1251 windows-1252 windows-1253 windows-1254
|
||||
windows-1255 windows-1256 windows-1257 windows-1258 x-mac-cyrillic
|
||||
gb18030 hz-gb-2312 big5 euc-jp iso-2022-jp shift_jis euc-kr
|
||||
replacement utf-16be utf-16le x-user-defined
|
||||
|
||||
(Some encodings may be supported under other names, e.g. ascii,
|
||||
iso-8859-1, etc. See [Encoding](https://encoding.spec.whatwg.org/) for
|
||||
additional labels for each encoding.)
|
||||
|
||||
Encodings other than **utf-8**, **utf-16le** and **utf-16be** require
|
||||
an additional `encoding-indexes.js` file to be included. It is rather
|
||||
large (596kB uncompressed, 188kB gzipped); portions may be deleted if
|
||||
support for some encodings is not required.
|
||||
|
||||
### Non-Standard Behavior ###
|
||||
|
||||
As required by the specification, only encoding to **utf-8** is
|
||||
supported. If you want to try it out, you can force a non-standard
|
||||
behavior by passing the `NONSTANDARD_allowLegacyEncoding` option to
|
||||
TextEncoder and a label. For example:
|
||||
|
||||
```js
|
||||
var uint8array = new TextEncoder(
|
||||
'windows-1252', { NONSTANDARD_allowLegacyEncoding: true }).encode(text);
|
||||
```
|
||||
|
||||
But note that the above won't work if you're using the polyfill in a
|
||||
browser that natively supports the TextEncoder API natively, since the
|
||||
polyfill won't be used!
|
||||
|
||||
You can force the polyfill to be used by using this before the polyfill:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.TextEncoder = window.TextDecoder = null;
|
||||
</script>
|
||||
```
|
||||
|
||||
To support the legacy encodings (which may be stateful), the
|
||||
TextEncoder `encode()` method accepts an optional dictionary and
|
||||
`stream` option, e.g. `encoder.encode(string, {stream: true});` This
|
||||
is not needed for standard encoding since the input is always in
|
||||
complete code points.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// See LICENSE.md for more information.
|
||||
|
||||
var encoding = require("./lib/encoding.js");
|
||||
|
||||
module.exports = {
|
||||
TextEncoder: encoding.TextEncoder,
|
||||
TextDecoder: encoding.TextDecoder,
|
||||
};
|
||||
+47
File diff suppressed because one or more lines are too long
+3313
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@sinonjs/text-encoding",
|
||||
"scripts": {
|
||||
"postpublish": "git push --tags"
|
||||
},
|
||||
"author": "Joshua Bell <inexorabletash@gmail.com>",
|
||||
"contributors": [
|
||||
"Joshua Bell <inexorabletash@gmail.com>",
|
||||
"Rick Eyre <rick.eyre@outlook.com>",
|
||||
"Eugen Podaru <eugen.podaru@live.com>",
|
||||
"Filip Dupanović <filip.dupanovic@gmail.com>",
|
||||
"Anne van Kesteren <annevk@annevk.nl>",
|
||||
"Author: Francis Avila <francisga@gmail.com>",
|
||||
"Michael J. Ryan <tracker1@gmail.com>",
|
||||
"Pierre Queinnec <pierre@queinnec.org>",
|
||||
"Zack Weinberg <zackw@panix.com>"
|
||||
],
|
||||
"version": "0.7.3",
|
||||
"description": "Polyfill for the Encoding Living Standard's API.",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib/encoding.js",
|
||||
"lib/encoding-indexes.js"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sinonjs/text-encoding.git"
|
||||
},
|
||||
"keywords": [
|
||||
"encoding",
|
||||
"decoding",
|
||||
"living standard"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/sinonjs/text-encoding/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sinonjs/text-encoding",
|
||||
"license": "(Unlicense OR Apache-2.0)"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Supabase
|
||||
|
||||
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.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# `auth-js`
|
||||
|
||||
An isomorphic JavaScript client library for the [Supabase Auth](https://github.com/supabase/auth) API.
|
||||
|
||||
## Docs
|
||||
|
||||
- Using `auth-js`: https://supabase.com/docs/reference/javascript/auth-signup
|
||||
- TypeDoc: https://supabase.github.io/auth-js/v2
|
||||
|
||||
## Quick start
|
||||
|
||||
Install
|
||||
|
||||
```bash
|
||||
npm install --save @supabase/auth-js
|
||||
```
|
||||
|
||||
Usage
|
||||
|
||||
```js
|
||||
import { AuthClient } from '@supabase/auth-js'
|
||||
|
||||
const GOTRUE_URL = 'http://localhost:9999'
|
||||
|
||||
const auth = new AuthClient({ url: GOTRUE_URL })
|
||||
```
|
||||
|
||||
- `signUp()`: https://supabase.io/docs/reference/javascript/auth-signup
|
||||
- `signIn()`: https://supabase.io/docs/reference/javascript/auth-signin
|
||||
- `signOut()`: https://supabase.io/docs/reference/javascript/auth-signout
|
||||
|
||||
### Custom `fetch` implementation
|
||||
|
||||
`auth-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests, but an alternative `fetch` implementation can be provided as an option. This is most useful in environments where `cross-fetch` is not compatible, for instance Cloudflare Workers:
|
||||
|
||||
```js
|
||||
import { AuthClient } from '@supabase/auth-js'
|
||||
|
||||
const AUTH_URL = 'http://localhost:9999'
|
||||
|
||||
const auth = new AuthClient({ url: AUTH_URL, fetch: fetch })
|
||||
```
|
||||
|
||||
## Sponsors
|
||||
|
||||
We are building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don’t exist we build them and open source them ourselves.
|
||||
|
||||
[](https://github.com/sponsors/supabase)
|
||||
|
||||

|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GoTrueAdminApi_1 = __importDefault(require("./GoTrueAdminApi"));
|
||||
const AuthAdminApi = GoTrueAdminApi_1.default;
|
||||
exports.default = AuthAdminApi;
|
||||
//# sourceMappingURL=AuthAdminApi.js.map
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const GoTrueClient_1 = __importDefault(require("./GoTrueClient"));
|
||||
const AuthClient = GoTrueClient_1.default;
|
||||
exports.default = AuthClient;
|
||||
//# sourceMappingURL=AuthClient.js.map
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
"use strict";
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fetch_1 = require("./lib/fetch");
|
||||
const helpers_1 = require("./lib/helpers");
|
||||
const errors_1 = require("./lib/errors");
|
||||
class GoTrueAdminApi {
|
||||
constructor({ url = '', headers = {}, fetch, }) {
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
this.fetch = (0, helpers_1.resolveFetch)(fetch);
|
||||
this.mfa = {
|
||||
listFactors: this._listFactors.bind(this),
|
||||
deleteFactor: this._deleteFactor.bind(this),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Removes a logged-in session.
|
||||
* @param jwt A valid, logged-in JWT.
|
||||
* @param scope The logout sope.
|
||||
*/
|
||||
async signOut(jwt, scope = 'global') {
|
||||
try {
|
||||
await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/logout?scope=${scope}`, {
|
||||
headers: this.headers,
|
||||
jwt,
|
||||
noResolveJson: true,
|
||||
});
|
||||
return { data: null, error: null };
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: null, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends an invite link to an email address.
|
||||
* @param email The email address of the user.
|
||||
* @param options Additional options to be included when inviting.
|
||||
*/
|
||||
async inviteUserByEmail(email, options = {}) {
|
||||
try {
|
||||
return await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/invite`, {
|
||||
body: { email, data: options.data },
|
||||
headers: this.headers,
|
||||
redirectTo: options.redirectTo,
|
||||
xform: fetch_1._userResponse,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { user: null }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generates email links and OTPs to be sent via a custom email provider.
|
||||
* @param email The user's email.
|
||||
* @param options.password User password. For signup only.
|
||||
* @param options.data Optional user metadata. For signup only.
|
||||
* @param options.redirectTo The redirect url which should be appended to the generated link
|
||||
*/
|
||||
async generateLink(params) {
|
||||
try {
|
||||
const { options } = params, rest = __rest(params, ["options"]);
|
||||
const body = Object.assign(Object.assign({}, rest), options);
|
||||
if ('newEmail' in rest) {
|
||||
// replace newEmail with new_email in request body
|
||||
body.new_email = rest === null || rest === void 0 ? void 0 : rest.newEmail;
|
||||
delete body['newEmail'];
|
||||
}
|
||||
return await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/admin/generate_link`, {
|
||||
body: body,
|
||||
headers: this.headers,
|
||||
xform: fetch_1._generateLinkResponse,
|
||||
redirectTo: options === null || options === void 0 ? void 0 : options.redirectTo,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return {
|
||||
data: {
|
||||
properties: null,
|
||||
user: null,
|
||||
},
|
||||
error,
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// User Admin API
|
||||
/**
|
||||
* Creates a new user.
|
||||
* This function should only be called on a server. Never expose your `service_role` key in the browser.
|
||||
*/
|
||||
async createUser(attributes) {
|
||||
try {
|
||||
return await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/admin/users`, {
|
||||
body: attributes,
|
||||
headers: this.headers,
|
||||
xform: fetch_1._userResponse,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { user: null }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get a list of users.
|
||||
*
|
||||
* This function should only be called on a server. Never expose your `service_role` key in the browser.
|
||||
* @param params An object which supports `page` and `perPage` as numbers, to alter the paginated results.
|
||||
*/
|
||||
async listUsers(params) {
|
||||
var _a, _b, _c, _d, _e, _f, _g;
|
||||
try {
|
||||
const pagination = { nextPage: null, lastPage: 0, total: 0 };
|
||||
const response = await (0, fetch_1._request)(this.fetch, 'GET', `${this.url}/admin/users`, {
|
||||
headers: this.headers,
|
||||
noResolveJson: true,
|
||||
query: {
|
||||
page: (_b = (_a = params === null || params === void 0 ? void 0 : params.page) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '',
|
||||
per_page: (_d = (_c = params === null || params === void 0 ? void 0 : params.perPage) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : '',
|
||||
},
|
||||
xform: fetch_1._noResolveJsonResponse,
|
||||
});
|
||||
if (response.error)
|
||||
throw response.error;
|
||||
const users = await response.json();
|
||||
const total = (_e = response.headers.get('x-total-count')) !== null && _e !== void 0 ? _e : 0;
|
||||
const links = (_g = (_f = response.headers.get('link')) === null || _f === void 0 ? void 0 : _f.split(',')) !== null && _g !== void 0 ? _g : [];
|
||||
if (links.length > 0) {
|
||||
links.forEach((link) => {
|
||||
const page = parseInt(link.split(';')[0].split('=')[1].substring(0, 1));
|
||||
const rel = JSON.parse(link.split(';')[1].split('=')[1]);
|
||||
pagination[`${rel}Page`] = page;
|
||||
});
|
||||
pagination.total = parseInt(total);
|
||||
}
|
||||
return { data: Object.assign(Object.assign({}, users), pagination), error: null };
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { users: [] }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get user by id.
|
||||
*
|
||||
* @param uid The user's unique identifier
|
||||
*
|
||||
* This function should only be called on a server. Never expose your `service_role` key in the browser.
|
||||
*/
|
||||
async getUserById(uid) {
|
||||
try {
|
||||
return await (0, fetch_1._request)(this.fetch, 'GET', `${this.url}/admin/users/${uid}`, {
|
||||
headers: this.headers,
|
||||
xform: fetch_1._userResponse,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { user: null }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Updates the user data.
|
||||
*
|
||||
* @param attributes The data you want to update.
|
||||
*
|
||||
* This function should only be called on a server. Never expose your `service_role` key in the browser.
|
||||
*/
|
||||
async updateUserById(uid, attributes) {
|
||||
try {
|
||||
return await (0, fetch_1._request)(this.fetch, 'PUT', `${this.url}/admin/users/${uid}`, {
|
||||
body: attributes,
|
||||
headers: this.headers,
|
||||
xform: fetch_1._userResponse,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { user: null }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete a user. Requires a `service_role` key.
|
||||
*
|
||||
* @param id The user id you want to remove.
|
||||
* @param shouldSoftDelete If true, then the user will be soft-deleted from the auth schema. Soft deletion allows user identification from the hashed user ID but is not reversible.
|
||||
* Defaults to false for backward compatibility.
|
||||
*
|
||||
* This function should only be called on a server. Never expose your `service_role` key in the browser.
|
||||
*/
|
||||
async deleteUser(id, shouldSoftDelete = false) {
|
||||
try {
|
||||
return await (0, fetch_1._request)(this.fetch, 'DELETE', `${this.url}/admin/users/${id}`, {
|
||||
headers: this.headers,
|
||||
body: {
|
||||
should_soft_delete: shouldSoftDelete,
|
||||
},
|
||||
xform: fetch_1._userResponse,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: { user: null }, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async _listFactors(params) {
|
||||
try {
|
||||
const { data, error } = await (0, fetch_1._request)(this.fetch, 'GET', `${this.url}/admin/users/${params.userId}/factors`, {
|
||||
headers: this.headers,
|
||||
xform: (factors) => {
|
||||
return { data: { factors }, error: null };
|
||||
},
|
||||
});
|
||||
return { data, error };
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: null, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async _deleteFactor(params) {
|
||||
try {
|
||||
const data = await (0, fetch_1._request)(this.fetch, 'DELETE', `${this.url}/admin/users/${params.userId}/factors/${params.id}`, {
|
||||
headers: this.headers,
|
||||
});
|
||||
return { data, error: null };
|
||||
}
|
||||
catch (error) {
|
||||
if ((0, errors_1.isAuthError)(error)) {
|
||||
return { data: null, error };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = GoTrueAdminApi;
|
||||
//# sourceMappingURL=GoTrueAdminApi.js.map
|
||||
+1986
File diff suppressed because it is too large
Load Diff
+35
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.lockInternals = exports.NavigatorLockAcquireTimeoutError = exports.navigatorLock = exports.AuthClient = exports.AuthAdminApi = exports.GoTrueClient = exports.GoTrueAdminApi = void 0;
|
||||
const GoTrueAdminApi_1 = __importDefault(require("./GoTrueAdminApi"));
|
||||
exports.GoTrueAdminApi = GoTrueAdminApi_1.default;
|
||||
const GoTrueClient_1 = __importDefault(require("./GoTrueClient"));
|
||||
exports.GoTrueClient = GoTrueClient_1.default;
|
||||
const AuthAdminApi_1 = __importDefault(require("./AuthAdminApi"));
|
||||
exports.AuthAdminApi = AuthAdminApi_1.default;
|
||||
const AuthClient_1 = __importDefault(require("./AuthClient"));
|
||||
exports.AuthClient = AuthClient_1.default;
|
||||
__exportStar(require("./lib/types"), exports);
|
||||
__exportStar(require("./lib/errors"), exports);
|
||||
var locks_1 = require("./lib/locks");
|
||||
Object.defineProperty(exports, "navigatorLock", { enumerable: true, get: function () { return locks_1.navigatorLock; } });
|
||||
Object.defineProperty(exports, "NavigatorLockAcquireTimeoutError", { enumerable: true, get: function () { return locks_1.NavigatorLockAcquireTimeoutError; } });
|
||||
Object.defineProperty(exports, "lockInternals", { enumerable: true, get: function () { return locks_1.internals; } });
|
||||
//# sourceMappingURL=index.js.map
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.API_VERSIONS = exports.API_VERSION_HEADER_NAME = exports.NETWORK_FAILURE = exports.EXPIRY_MARGIN = exports.DEFAULT_HEADERS = exports.AUDIENCE = exports.STORAGE_KEY = exports.GOTRUE_URL = void 0;
|
||||
const version_1 = require("./version");
|
||||
exports.GOTRUE_URL = 'http://localhost:9999';
|
||||
exports.STORAGE_KEY = 'supabase.auth.token';
|
||||
exports.AUDIENCE = '';
|
||||
exports.DEFAULT_HEADERS = { 'X-Client-Info': `gotrue-js/${version_1.version}` };
|
||||
exports.EXPIRY_MARGIN = 10; // in seconds
|
||||
exports.NETWORK_FAILURE = {
|
||||
MAX_RETRIES: 10,
|
||||
RETRY_INTERVAL: 2, // in deciseconds
|
||||
};
|
||||
exports.API_VERSION_HEADER_NAME = 'X-Supabase-Api-Version';
|
||||
exports.API_VERSIONS = {
|
||||
'2024-01-01': {
|
||||
timestamp: Date.parse('2024-01-01T00:00:00.0Z'),
|
||||
name: '2024-01-01',
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=constants.js.map
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=error-codes.js.map
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isAuthWeakPasswordError = exports.AuthWeakPasswordError = exports.isAuthRetryableFetchError = exports.AuthRetryableFetchError = exports.AuthPKCEGrantCodeExchangeError = exports.isAuthImplicitGrantRedirectError = exports.AuthImplicitGrantRedirectError = exports.AuthInvalidCredentialsError = exports.AuthInvalidTokenResponseError = exports.isAuthSessionMissingError = exports.AuthSessionMissingError = exports.CustomAuthError = exports.AuthUnknownError = exports.isAuthApiError = exports.AuthApiError = exports.isAuthError = exports.AuthError = void 0;
|
||||
class AuthError extends Error {
|
||||
constructor(message, status, code) {
|
||||
super(message);
|
||||
this.__isAuthError = true;
|
||||
this.name = 'AuthError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
exports.AuthError = AuthError;
|
||||
function isAuthError(error) {
|
||||
return typeof error === 'object' && error !== null && '__isAuthError' in error;
|
||||
}
|
||||
exports.isAuthError = isAuthError;
|
||||
class AuthApiError extends AuthError {
|
||||
constructor(message, status, code) {
|
||||
super(message, status, code);
|
||||
this.name = 'AuthApiError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
exports.AuthApiError = AuthApiError;
|
||||
function isAuthApiError(error) {
|
||||
return isAuthError(error) && error.name === 'AuthApiError';
|
||||
}
|
||||
exports.isAuthApiError = isAuthApiError;
|
||||
class AuthUnknownError extends AuthError {
|
||||
constructor(message, originalError) {
|
||||
super(message);
|
||||
this.name = 'AuthUnknownError';
|
||||
this.originalError = originalError;
|
||||
}
|
||||
}
|
||||
exports.AuthUnknownError = AuthUnknownError;
|
||||
class CustomAuthError extends AuthError {
|
||||
constructor(message, name, status, code) {
|
||||
super(message, status, code);
|
||||
this.name = name;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
exports.CustomAuthError = CustomAuthError;
|
||||
class AuthSessionMissingError extends CustomAuthError {
|
||||
constructor() {
|
||||
super('Auth session missing!', 'AuthSessionMissingError', 400, undefined);
|
||||
}
|
||||
}
|
||||
exports.AuthSessionMissingError = AuthSessionMissingError;
|
||||
function isAuthSessionMissingError(error) {
|
||||
return isAuthError(error) && error.name === 'AuthSessionMissingError';
|
||||
}
|
||||
exports.isAuthSessionMissingError = isAuthSessionMissingError;
|
||||
class AuthInvalidTokenResponseError extends CustomAuthError {
|
||||
constructor() {
|
||||
super('Auth session or user missing', 'AuthInvalidTokenResponseError', 500, undefined);
|
||||
}
|
||||
}
|
||||
exports.AuthInvalidTokenResponseError = AuthInvalidTokenResponseError;
|
||||
class AuthInvalidCredentialsError extends CustomAuthError {
|
||||
constructor(message) {
|
||||
super(message, 'AuthInvalidCredentialsError', 400, undefined);
|
||||
}
|
||||
}
|
||||
exports.AuthInvalidCredentialsError = AuthInvalidCredentialsError;
|
||||
class AuthImplicitGrantRedirectError extends CustomAuthError {
|
||||
constructor(message, details = null) {
|
||||
super(message, 'AuthImplicitGrantRedirectError', 500, undefined);
|
||||
this.details = null;
|
||||
this.details = details;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
message: this.message,
|
||||
status: this.status,
|
||||
details: this.details,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.AuthImplicitGrantRedirectError = AuthImplicitGrantRedirectError;
|
||||
function isAuthImplicitGrantRedirectError(error) {
|
||||
return isAuthError(error) && error.name === 'AuthImplicitGrantRedirectError';
|
||||
}
|
||||
exports.isAuthImplicitGrantRedirectError = isAuthImplicitGrantRedirectError;
|
||||
class AuthPKCEGrantCodeExchangeError extends CustomAuthError {
|
||||
constructor(message, details = null) {
|
||||
super(message, 'AuthPKCEGrantCodeExchangeError', 500, undefined);
|
||||
this.details = null;
|
||||
this.details = details;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
message: this.message,
|
||||
status: this.status,
|
||||
details: this.details,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.AuthPKCEGrantCodeExchangeError = AuthPKCEGrantCodeExchangeError;
|
||||
class AuthRetryableFetchError extends CustomAuthError {
|
||||
constructor(message, status) {
|
||||
super(message, 'AuthRetryableFetchError', status, undefined);
|
||||
}
|
||||
}
|
||||
exports.AuthRetryableFetchError = AuthRetryableFetchError;
|
||||
function isAuthRetryableFetchError(error) {
|
||||
return isAuthError(error) && error.name === 'AuthRetryableFetchError';
|
||||
}
|
||||
exports.isAuthRetryableFetchError = isAuthRetryableFetchError;
|
||||
/**
|
||||
* This error is thrown on certain methods when the password used is deemed
|
||||
* weak. Inspect the reasons to identify what password strength rules are
|
||||
* inadequate.
|
||||
*/
|
||||
class AuthWeakPasswordError extends CustomAuthError {
|
||||
constructor(message, status, reasons) {
|
||||
super(message, 'AuthWeakPasswordError', status, 'weak_password');
|
||||
this.reasons = reasons;
|
||||
}
|
||||
}
|
||||
exports.AuthWeakPasswordError = AuthWeakPasswordError;
|
||||
function isAuthWeakPasswordError(error) {
|
||||
return isAuthError(error) && error.name === 'AuthWeakPasswordError';
|
||||
}
|
||||
exports.isAuthWeakPasswordError = isAuthWeakPasswordError;
|
||||
//# sourceMappingURL=errors.js.map
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
"use strict";
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports._noResolveJsonResponse = exports._generateLinkResponse = exports._ssoResponse = exports._userResponse = exports._sessionResponsePassword = exports._sessionResponse = exports._request = exports.handleError = void 0;
|
||||
const constants_1 = require("./constants");
|
||||
const helpers_1 = require("./helpers");
|
||||
const errors_1 = require("./errors");
|
||||
const _getErrorMessage = (err) => err.msg || err.message || err.error_description || err.error || JSON.stringify(err);
|
||||
const NETWORK_ERROR_CODES = [502, 503, 504];
|
||||
async function handleError(error) {
|
||||
var _a;
|
||||
if (!(0, helpers_1.looksLikeFetchResponse)(error)) {
|
||||
throw new errors_1.AuthRetryableFetchError(_getErrorMessage(error), 0);
|
||||
}
|
||||
if (NETWORK_ERROR_CODES.includes(error.status)) {
|
||||
// status in 500...599 range - server had an error, request might be retryed.
|
||||
throw new errors_1.AuthRetryableFetchError(_getErrorMessage(error), error.status);
|
||||
}
|
||||
let data;
|
||||
try {
|
||||
data = await error.json();
|
||||
}
|
||||
catch (e) {
|
||||
throw new errors_1.AuthUnknownError(_getErrorMessage(e), e);
|
||||
}
|
||||
let errorCode = undefined;
|
||||
const responseAPIVersion = (0, helpers_1.parseResponseAPIVersion)(error);
|
||||
if (responseAPIVersion &&
|
||||
responseAPIVersion.getTime() >= constants_1.API_VERSIONS['2024-01-01'].timestamp &&
|
||||
typeof data === 'object' &&
|
||||
data &&
|
||||
typeof data.code === 'string') {
|
||||
errorCode = data.code;
|
||||
}
|
||||
else if (typeof data === 'object' && data && typeof data.error_code === 'string') {
|
||||
errorCode = data.error_code;
|
||||
}
|
||||
if (!errorCode) {
|
||||
// Legacy support for weak password errors, when there were no error codes
|
||||
if (typeof data === 'object' &&
|
||||
data &&
|
||||
typeof data.weak_password === 'object' &&
|
||||
data.weak_password &&
|
||||
Array.isArray(data.weak_password.reasons) &&
|
||||
data.weak_password.reasons.length &&
|
||||
data.weak_password.reasons.reduce((a, i) => a && typeof i === 'string', true)) {
|
||||
throw new errors_1.AuthWeakPasswordError(_getErrorMessage(data), error.status, data.weak_password.reasons);
|
||||
}
|
||||
}
|
||||
else if (errorCode === 'weak_password') {
|
||||
throw new errors_1.AuthWeakPasswordError(_getErrorMessage(data), error.status, ((_a = data.weak_password) === null || _a === void 0 ? void 0 : _a.reasons) || []);
|
||||
}
|
||||
else if (errorCode === 'session_not_found') {
|
||||
// The `session_id` inside the JWT does not correspond to a row in the
|
||||
// `sessions` table. This usually means the user has signed out, has been
|
||||
// deleted, or their session has somehow been terminated.
|
||||
throw new errors_1.AuthSessionMissingError();
|
||||
}
|
||||
throw new errors_1.AuthApiError(_getErrorMessage(data), error.status || 500, errorCode);
|
||||
}
|
||||
exports.handleError = handleError;
|
||||
const _getRequestParams = (method, options, parameters, body) => {
|
||||
const params = { method, headers: (options === null || options === void 0 ? void 0 : options.headers) || {} };
|
||||
if (method === 'GET') {
|
||||
return params;
|
||||
}
|
||||
params.headers = Object.assign({ 'Content-Type': 'application/json;charset=UTF-8' }, options === null || options === void 0 ? void 0 : options.headers);
|
||||
params.body = JSON.stringify(body);
|
||||
return Object.assign(Object.assign({}, params), parameters);
|
||||
};
|
||||
async function _request(fetcher, method, url, options) {
|
||||
var _a;
|
||||
const headers = Object.assign({}, options === null || options === void 0 ? void 0 : options.headers);
|
||||
if (!headers[constants_1.API_VERSION_HEADER_NAME]) {
|
||||
headers[constants_1.API_VERSION_HEADER_NAME] = constants_1.API_VERSIONS['2024-01-01'].name;
|
||||
}
|
||||
if (options === null || options === void 0 ? void 0 : options.jwt) {
|
||||
headers['Authorization'] = `Bearer ${options.jwt}`;
|
||||
}
|
||||
const qs = (_a = options === null || options === void 0 ? void 0 : options.query) !== null && _a !== void 0 ? _a : {};
|
||||
if (options === null || options === void 0 ? void 0 : options.redirectTo) {
|
||||
qs['redirect_to'] = options.redirectTo;
|
||||
}
|
||||
const queryString = Object.keys(qs).length ? '?' + new URLSearchParams(qs).toString() : '';
|
||||
const data = await _handleRequest(fetcher, method, url + queryString, {
|
||||
headers,
|
||||
noResolveJson: options === null || options === void 0 ? void 0 : options.noResolveJson,
|
||||
}, {}, options === null || options === void 0 ? void 0 : options.body);
|
||||
return (options === null || options === void 0 ? void 0 : options.xform) ? options === null || options === void 0 ? void 0 : options.xform(data) : { data: Object.assign({}, data), error: null };
|
||||
}
|
||||
exports._request = _request;
|
||||
async function _handleRequest(fetcher, method, url, options, parameters, body) {
|
||||
const requestParams = _getRequestParams(method, options, parameters, body);
|
||||
let result;
|
||||
try {
|
||||
result = await fetcher(url, Object.assign({}, requestParams));
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
// fetch failed, likely due to a network or CORS error
|
||||
throw new errors_1.AuthRetryableFetchError(_getErrorMessage(e), 0);
|
||||
}
|
||||
if (!result.ok) {
|
||||
await handleError(result);
|
||||
}
|
||||
if (options === null || options === void 0 ? void 0 : options.noResolveJson) {
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
return await result.json();
|
||||
}
|
||||
catch (e) {
|
||||
await handleError(e);
|
||||
}
|
||||
}
|
||||
function _sessionResponse(data) {
|
||||
var _a;
|
||||
let session = null;
|
||||
if (hasSession(data)) {
|
||||
session = Object.assign({}, data);
|
||||
if (!data.expires_at) {
|
||||
session.expires_at = (0, helpers_1.expiresAt)(data.expires_in);
|
||||
}
|
||||
}
|
||||
const user = (_a = data.user) !== null && _a !== void 0 ? _a : data;
|
||||
return { data: { session, user }, error: null };
|
||||
}
|
||||
exports._sessionResponse = _sessionResponse;
|
||||
function _sessionResponsePassword(data) {
|
||||
const response = _sessionResponse(data);
|
||||
if (!response.error &&
|
||||
data.weak_password &&
|
||||
typeof data.weak_password === 'object' &&
|
||||
Array.isArray(data.weak_password.reasons) &&
|
||||
data.weak_password.reasons.length &&
|
||||
data.weak_password.message &&
|
||||
typeof data.weak_password.message === 'string' &&
|
||||
data.weak_password.reasons.reduce((a, i) => a && typeof i === 'string', true)) {
|
||||
response.data.weak_password = data.weak_password;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
exports._sessionResponsePassword = _sessionResponsePassword;
|
||||
function _userResponse(data) {
|
||||
var _a;
|
||||
const user = (_a = data.user) !== null && _a !== void 0 ? _a : data;
|
||||
return { data: { user }, error: null };
|
||||
}
|
||||
exports._userResponse = _userResponse;
|
||||
function _ssoResponse(data) {
|
||||
return { data, error: null };
|
||||
}
|
||||
exports._ssoResponse = _ssoResponse;
|
||||
function _generateLinkResponse(data) {
|
||||
const { action_link, email_otp, hashed_token, redirect_to, verification_type } = data, rest = __rest(data, ["action_link", "email_otp", "hashed_token", "redirect_to", "verification_type"]);
|
||||
const properties = {
|
||||
action_link,
|
||||
email_otp,
|
||||
hashed_token,
|
||||
redirect_to,
|
||||
verification_type,
|
||||
};
|
||||
const user = Object.assign({}, rest);
|
||||
return {
|
||||
data: {
|
||||
properties,
|
||||
user,
|
||||
},
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
exports._generateLinkResponse = _generateLinkResponse;
|
||||
function _noResolveJsonResponse(data) {
|
||||
return data;
|
||||
}
|
||||
exports._noResolveJsonResponse = _noResolveJsonResponse;
|
||||
/**
|
||||
* hasSession checks if the response object contains a valid session
|
||||
* @param data A response object
|
||||
* @returns true if a session is in the response
|
||||
*/
|
||||
function hasSession(data) {
|
||||
return data.access_token && data.refresh_token && data.expires_in;
|
||||
}
|
||||
//# sourceMappingURL=fetch.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user