Socket
Socket
Sign inDemoInstall

@codemirror/state

Package Overview
Dependencies
Maintainers
2
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@codemirror/state - npm Package Compare versions

Comparing version 0.19.9 to 0.20.0

20

CHANGELOG.md

@@ -0,1 +1,21 @@

## 0.20.0 (2022-04-20)
### Breaking changes
The deprecated precedence names `fallback`, `extend`, and `override` were removed from the library.
### Bug fixes
Fix a bug where, if an extension value occurs multiple times, its lowest, rather than highest precedence is used.
Fix an issue where facets with computed inputs would unneccesarily have their outputs recreated on state reconfiguration.
Fix a bug in the order in which new values for state fields and facets were computed, which could cause dynamic facets to hold the wrong value in some situations.
### New features
The exports from @codemirror/rangeset now live in this package.
The exports from @codemirror/text now live in this package.
## 0.19.9 (2022-02-16)

@@ -2,0 +22,0 @@

573

dist/index.d.ts

@@ -1,3 +0,134 @@

import { Text } from '@codemirror/text';
export { Text } from '@codemirror/text';
/**
A text iterator iterates over a sequence of strings. When
iterating over a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) document, result values will
either be lines or line breaks.
*/
interface TextIterator extends Iterator<string>, Iterable<string> {
/**
Retrieve the next string. Optionally skip a given number of
positions after the current position. Always returns the object
itself.
*/
next(skip?: number): this;
/**
The current string. Will be the empty string when the cursor is
at its end or `next` hasn't been called on it yet.
*/
value: string;
/**
Whether the end of the iteration has been reached. You should
probably check this right after calling `next`.
*/
done: boolean;
/**
Whether the current string represents a line break.
*/
lineBreak: boolean;
}
/**
The data structure for documents. @nonabstract
*/
declare abstract class Text implements Iterable<string> {
/**
The length of the string.
*/
abstract readonly length: number;
/**
The number of lines in the string (always >= 1).
*/
abstract readonly lines: number;
/**
Get the line description around the given position.
*/
lineAt(pos: number): Line;
/**
Get the description for the given (1-based) line number.
*/
line(n: number): Line;
/**
Replace a range of the text with the given content.
*/
replace(from: number, to: number, text: Text): Text;
/**
Append another document to this one.
*/
append(other: Text): Text;
/**
Retrieve the text between the given points.
*/
slice(from: number, to?: number): Text;
/**
Retrieve a part of the document as a string
*/
abstract sliceString(from: number, to?: number, lineSep?: string): string;
/**
Test whether this text is equal to another instance.
*/
eq(other: Text): boolean;
/**
Iterate over the text. When `dir` is `-1`, iteration happens
from end to start. This will return lines and the breaks between
them as separate strings.
*/
iter(dir?: 1 | -1): TextIterator;
/**
Iterate over a range of the text. When `from` > `to`, the
iterator will run in reverse.
*/
iterRange(from: number, to?: number): TextIterator;
/**
Return a cursor that iterates over the given range of lines,
_without_ returning the line breaks between, and yielding empty
strings for empty lines.
When `from` and `to` are given, they should be 1-based line numbers.
*/
iterLines(from?: number, to?: number): TextIterator;
/**
Convert the document to an array of lines (which can be
deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)).
*/
toJSON(): string[];
/**
If this is a branch node, `children` will hold the `Text`
objects that it is made up of. For leaf nodes, this holds null.
*/
abstract readonly children: readonly Text[] | null;
[Symbol.iterator]: () => Iterator<string>;
/**
Create a `Text` instance for the given array of lines.
*/
static of(text: readonly string[]): Text;
/**
The empty document.
*/
static empty: Text;
}
/**
This type describes a line in the document. It is created
on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt).
*/
declare class Line {
/**
The position of the start of the line.
*/
readonly from: number;
/**
The position at the end of the line (_before_ the line break,
or at the end of document for the last line).
*/
readonly to: number;
/**
This line's line number (1-based).
*/
readonly number: number;
/**
The line's content.
*/
readonly text: string;
/**
The length of the line (not including any line break after it).
*/
get length(): number;
}

@@ -45,3 +176,5 @@ /**

/**
Iterate over the unchanged parts left by these changes.
Iterate over the unchanged parts left by these changes. `posA`
provides the position of the range in the old document, `posB`
the new position in the changed document.
*/

@@ -53,2 +186,5 @@ iterGaps(f: (posA: number, posB: number, length: number) => void): void;

variant that also provides you with the inserted text.)
`fromA`/`toA` provides the extent of the change in the starting
document, `fromB`/`toB` the extent of the replacement in the
changed document.

@@ -349,5 +485,5 @@ When `individual` is true, adjacent changes (which are kept

not given, the array of input values becomes the output. This
will immediately be called on creating the facet, with an empty
array, to compute the facet's default value when no inputs are
present.
function will immediately be called on creating the facet, with
an empty array, to compute the facet's default value when no
inputs are present.
*/

@@ -368,3 +504,3 @@ combine?: (value: readonly Input[]) => Output;

/**
Static facets can not contain dynamic inputs.
Forbids dynamic inputs to this facet.
*/

@@ -386,6 +522,6 @@ static?: boolean;

Examples of facets are the [theme](https://codemirror.net/6/docs/ref/#view.EditorView^theme) styles
associated with an editor or the [tab
size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) (which is reduced to a single
value, using the input with the hightest precedence).
Examples of uses of facets are the [tab
size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor
attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update
listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener).
*/

@@ -400,3 +536,3 @@ declare class Facet<Input, Output = readonly Input[]> {

/**
Returns an extension that adds the given value for this facet.
Returns an extension that adds the given value to this facet.
*/

@@ -410,5 +546,4 @@ of(value: Input): Extension;

In most cases, you'll want to use the
[`provide`](https://codemirror.net/6/docs/ref/#state.StateField^define^config.provide) option when
defining a field instead.
In cases where your value depends only on a single field, you'll
want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead.
*/

@@ -427,3 +562,3 @@ compute(deps: readonly Slot<any>[], get: (state: EditorState) => Input): Extension;

*/
from(field: StateField<Input>): Extension;
from<T extends Input>(field: StateField<T>): Extension;
from<T>(field: StateField<T>, get: (value: T) => Input): Extension;

@@ -450,8 +585,8 @@ }

/**
Provide values for facets based on the value of this field. The
given function will be called once with the initialized field. It
will usually want to call some facet's
[`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method to create facet inputs from
this field, but can also return other extensions that should be
enabled by this field.
Provide extensions based on this field. The given function will
be called once with the initialized field. It will usually want
to call some facet's [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method to
create facet inputs from this field, but can also return other
extensions that should be enabled when the field is present in a
configuration.
*/

@@ -523,10 +658,11 @@ provide?: (field: StateField<Value>) => Extension;

/**
The lowest precedence level. Meant for things that should end up
near the end of the extension order.
The highest precedence level, for extensions that should end up
near the start of the precedence ordering.
*/
lowest: (ext: Extension) => Extension;
highest: (ext: Extension) => Extension;
/**
A lower-than-default precedence, for extensions.
A higher-than-default precedence, for extensions that should
come before those with default precedence.
*/
low: (ext: Extension) => Extension;
high: (ext: Extension) => Extension;
/**

@@ -538,23 +674,10 @@ The default precedence, which is also used for extensions

/**
A higher-than-default precedence, for extensions that should
come before those with default precedence.
A lower-than-default precedence.
*/
high: (ext: Extension) => Extension;
low: (ext: Extension) => Extension;
/**
The highest precedence level, for extensions that should end up
near the start of the precedence ordering.
The lowest precedence level. Meant for things that should end up
near the end of the extension order.
*/
highest: (ext: Extension) => Extension;
/**
Backwards-compatible synonym for `Prec.lowest`.
*/
fallback: (ext: Extension) => Extension;
/**
Backwards-compatible synonym for `Prec.high`.
*/
extend: (ext: Extension) => Extension;
/**
Backwards-compatible synonym for `Prec.highest`.
*/
override: (ext: Extension) => Extension;
lowest: (ext: Extension) => Extension;
};

@@ -717,3 +840,3 @@ /**

/**
Shorthand for `annotations: `[`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)[`.of(...)`.
Shorthand for `annotations:` [`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)`.of(...)`.
*/

@@ -730,3 +853,5 @@ userEvent?: string;

filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter). You can set this
to `false` to disable that.
to `false` to disable that. This can be necessary for
transactions that, for example, include annotations that must be
kept consistent with their changes.
*/

@@ -749,3 +874,5 @@ filter?: boolean;

or have other effects. Create a transaction by calling
[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update).
[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately
dispatch one by calling
[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch).
*/

@@ -793,3 +920,3 @@ declare class Transaction {

The new state created by the transaction. Computed on demand
(but retained for subsequent access), so itis recommended not to
(but retained for subsequent access), so it is recommended not to
access it in [transaction

@@ -823,3 +950,4 @@ filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible.

/**
Annotation used to store transaction timestamps.
Annotation used to store transaction timestamps. Automatically
added to every transaction, holding `Date.now()`.
*/

@@ -898,3 +1026,3 @@ static time: AnnotationType<number>;

facet](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)), or an instance of
the [`Text`](https://codemirror.net/6/docs/ref/#text.Text) class (which is what the state will use
the [`Text`](https://codemirror.net/6/docs/ref/#state.Text) class (which is what the state will use
to represent the document).

@@ -961,7 +1089,3 @@ */

*/
replaceSelection(text: string | Text): {
changes: ChangeSet;
selection: EditorSelection;
effects: readonly StateEffect<any>[];
};
replaceSelection(text: string | Text): TransactionSpec;
/**

@@ -996,3 +1120,3 @@ Create a set of changes and a new selection by running the given

separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a
[`Text`](https://codemirror.net/6/docs/ref/#text.Text) instance from the given string.
[`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string.
*/

@@ -1119,3 +1243,3 @@ toText(string: string): Text;

Return a function that can categorize strings (expected to
represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#text.findClusterBreak))
represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak))
into one of:

@@ -1147,3 +1271,3 @@

suppressed. Such ranges are represented as an array of numbers,
with each pair of two number indicating the start and end of a
with each pair of two numbers indicating the start and end of a
range. So for example `[10, 20, 100, 110]` suppresses changes

@@ -1179,3 +1303,3 @@ between 10 and 20, and between 100 and 110.

[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type
of filter runs even the transaction has disabled regular
of filter runs even if the transaction has disabled regular
[filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable

@@ -1185,3 +1309,3 @@ for effects that don't need to touch the changes or selection,

Extenders run _after_ filters, when both are applied.
Extenders run _after_ filters, when both are present.
*/

@@ -1203,7 +1327,10 @@ static transactionExtender: Facet<(tr: Transaction) => Pick<TransactionSpec, "effects" | "annotations"> | null, readonly ((tr: Transaction) => Pick<TransactionSpec, "effects" | "annotations"> | null)[]>;

Utility function for combining behaviors to fill in a config
object from an array of provided configs. Will, by default, error
object from an array of provided configs. `defaults` should hold
default values for all optional fields in `Config`.
The function will, by default, error
when a field gets two values that aren't `===`-equal, but you can
provide combine functions per field to do something else.
*/
declare function combineConfig<Config>(configs: readonly Partial<Config>[], defaults: Partial<Config>, // Should hold only the optional properties of Config, but I haven't managed to express that
declare function combineConfig<Config extends object>(configs: readonly Partial<Config>[], defaults: Partial<Config>, // Should hold only the optional properties of Config, but I haven't managed to express that
combine?: {

@@ -1213,2 +1340,320 @@ [P in keyof Config]?: (first: Config[P], second: Config[P]) => Config[P];

export { Annotation, AnnotationType, ChangeDesc, ChangeSet, ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, EditorStateConfig, Extension, Facet, MapMode, Prec, SelectionRange, StateCommand, StateEffect, StateEffectType, StateField, Transaction, TransactionSpec, combineConfig };
/**
Each range is associated with a value, which must inherit from
this class.
*/
declare abstract class RangeValue {
/**
Compare this value with another value. Used when comparing
rangesets. The default implementation compares by identity.
Unless you are only creating a fixed number of unique instances
of your value type, it is a good idea to implement this
properly.
*/
eq(other: RangeValue): boolean;
/**
The bias value at the start of the range. Determines how the
range is positioned relative to other ranges starting at this
position. Defaults to 0.
*/
startSide: number;
/**
The bias value at the end of the range. Defaults to 0.
*/
endSide: number;
/**
The mode with which the location of the range should be mapped
when its `from` and `to` are the same, to decide whether a
change deletes the range. Defaults to `MapMode.TrackDel`.
*/
mapMode: MapMode;
/**
Determines whether this value marks a point range. Regular
ranges affect the part of the document they cover, and are
meaningless when empty. Point ranges have a meaning on their
own. When non-empty, a point range is treated as atomic and
shadows any ranges contained in it.
*/
point: boolean;
/**
Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value.
*/
range(from: number, to?: number): Range<this>;
}
/**
A range associates a value with a range of positions.
*/
declare class Range<T extends RangeValue> {
/**
The range's start position.
*/
readonly from: number;
/**
Its end position.
*/
readonly to: number;
/**
The value associated with this range.
*/
readonly value: T;
}
/**
Collection of methods used when comparing range sets.
*/
interface RangeComparator<T extends RangeValue> {
/**
Notifies the comparator that a range (in positions in the new
document) has the given sets of values associated with it, which
are different in the old (A) and new (B) sets.
*/
compareRange(from: number, to: number, activeA: T[], activeB: T[]): void;
/**
Notification for a changed (or inserted, or deleted) point range.
*/
comparePoint(from: number, to: number, pointA: T | null, pointB: T | null): void;
}
/**
Methods used when iterating over the spans created by a set of
ranges. The entire iterated range will be covered with either
`span` or `point` calls.
*/
interface SpanIterator<T extends RangeValue> {
/**
Called for any ranges not covered by point decorations. `active`
holds the values that the range is marked with (and may be
empty). `openStart` indicates how many of those ranges are open
(continued) at the start of the span.
*/
span(from: number, to: number, active: readonly T[], openStart: number): void;
/**
Called when going over a point decoration. The active range
decorations that cover the point and have a higher precedence
are provided in `active`. The open count in `openStart` counts
the number of those ranges that started before the point and. If
the point started before the iterated range, `openStart` will be
`active.length + 1` to signal this.
*/
point(from: number, to: number, value: T, active: readonly T[], openStart: number, index: number): void;
}
/**
A range cursor is an object that moves to the next range every
time you call `next` on it. Note that, unlike ES6 iterators, these
start out pointing at the first element, so you should call `next`
only after reading the first range (if any).
*/
interface RangeCursor<T> {
/**
Move the iterator forward.
*/
next: () => void;
/**
The next range's value. Holds `null` when the cursor has reached
its end.
*/
value: T | null;
/**
The next range's start position.
*/
from: number;
/**
The next end position.
*/
to: number;
}
declare type RangeSetUpdate<T extends RangeValue> = {
/**
An array of ranges to add. If given, this should be sorted by
`from` position and `startSide` unless
[`sort`](https://codemirror.net/6/docs/ref/#state.RangeSet.update^updateSpec.sort) is given as
`true`.
*/
add?: readonly Range<T>[];
/**
Indicates whether the library should sort the ranges in `add`.
Defaults to `false`.
*/
sort?: boolean;
/**
Filter the ranges already in the set. Only those for which this
function returns `true` are kept.
*/
filter?: (from: number, to: number, value: T) => boolean;
/**
Can be used to limit the range on which the filter is
applied. Filtering only a small range, as opposed to the entire
set, can make updates cheaper.
*/
filterFrom?: number;
/**
The end position to apply the filter to.
*/
filterTo?: number;
};
/**
A range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a
way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and
[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data
structure.
*/
declare class RangeSet<T extends RangeValue> {
/**
The number of ranges in the set.
*/
get size(): number;
/**
Update the range set, optionally adding new ranges or filtering
out existing ones.
(Note: The type parameter is just there as a kludge to work
around TypeScript variance issues that prevented `RangeSet<X>`
from being a subtype of `RangeSet<Y>` when `X` is a subtype of
`Y`.)
*/
update<U extends T>(updateSpec: RangeSetUpdate<U>): RangeSet<T>;
/**
Map this range set through a set of changes, return the new set.
*/
map(changes: ChangeDesc): RangeSet<T>;
/**
Iterate over the ranges that touch the region `from` to `to`,
calling `f` for each. There is no guarantee that the ranges will
be reported in any specific order. When the callback returns
`false`, iteration stops.
*/
between(from: number, to: number, f: (from: number, to: number, value: T) => void | false): void;
/**
Iterate over the ranges in this set, in order, including all
ranges that end at or after `from`.
*/
iter(from?: number): RangeCursor<T>;
/**
Iterate over the ranges in a collection of sets, in order,
starting from `from`.
*/
static iter<T extends RangeValue>(sets: readonly RangeSet<T>[], from?: number): RangeCursor<T>;
/**
Iterate over two groups of sets, calling methods on `comparator`
to notify it of possible differences.
*/
static compare<T extends RangeValue>(oldSets: readonly RangeSet<T>[], newSets: readonly RangeSet<T>[],
/**
This indicates how the underlying data changed between these
ranges, and is needed to synchronize the iteration. `from` and
`to` are coordinates in the _new_ space, after these changes.
*/
textDiff: ChangeDesc, comparator: RangeComparator<T>,
/**
Can be used to ignore all non-point ranges, and points below
the given size. When -1, all ranges are compared.
*/
minPointSize?: number): void;
/**
Compare the contents of two groups of range sets, returning true
if they are equivalent in the given range.
*/
static eq<T extends RangeValue>(oldSets: readonly RangeSet<T>[], newSets: readonly RangeSet<T>[], from?: number, to?: number): boolean;
/**
Iterate over a group of range sets at the same time, notifying
the iterator about the ranges covering every given piece of
content. Returns the open count (see
[`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end
of the iteration.
*/
static spans<T extends RangeValue>(sets: readonly RangeSet<T>[], from: number, to: number, iterator: SpanIterator<T>,
/**
When given and greater than -1, only points of at least this
size are taken into account.
*/
minPointSize?: number): number;
/**
Create a range set for the given range or array of ranges. By
default, this expects the ranges to be _sorted_ (by start
position and, if two start at the same position,
`value.startSide`). You can pass `true` as second argument to
cause the method to sort them.
*/
static of<T extends RangeValue>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
/**
The empty set of ranges.
*/
static empty: RangeSet<any>;
}
/**
A range set builder is a data structure that helps build up a
[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating
an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects.
*/
declare class RangeSetBuilder<T extends RangeValue> {
private chunks;
private chunkPos;
private chunkStart;
private last;
private lastFrom;
private lastTo;
private from;
private to;
private value;
private maxPoint;
private setMaxPoint;
private nextLayer;
private finishChunk;
/**
Create an empty builder.
*/
constructor();
/**
Add a range. Ranges should be added in sorted (by `from` and
`value.startSide`) order.
*/
add(from: number, to: number, value: T): void;
/**
Finish the range set. Returns the new set. The builder can't be
used anymore after this has been called.
*/
finish(): RangeSet<T>;
}
/**
Returns a next grapheme cluster break _after_ (not equal to)
`pos`, if `forward` is true, or before otherwise. Returns `pos`
itself if no further cluster break is available in the string.
Moves across surrogate pairs, extending characters (when
`includeExtending` is true), characters joined with zero-width
joiners, and flag emoji.
*/
declare function findClusterBreak(str: string, pos: number, forward?: boolean, includeExtending?: boolean): number;
/**
Find the code point at the given position in a string (like the
[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)
string method).
*/
declare function codePointAt(str: string, pos: number): number;
/**
Given a Unicode codepoint, return the JavaScript string that
respresents it (like
[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)).
*/
declare function fromCodePoint(code: number): string;
/**
The first character that takes up two positions in a JavaScript
string. It is often useful to compare with this after calling
`codePointAt`, to figure out whether your character takes up 1 or
2 index positions.
*/
declare function codePointSize(code: number): 1 | 2;
/**
Count the column position at the given offset into the string,
taking extending characters and tab size into account.
*/
declare function countColumn(string: string, tabSize: number, to?: number): number;
/**
Find the offset that corresponds to the given column position in a
string, taking extending characters and tab size into account. By
default, the string length is returned when it is too short to
reach the column. Pass `strict` true to make it return -1 in that
situation.
*/
declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number;
export { Annotation, AnnotationType, ChangeDesc, ChangeSet, ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, EditorStateConfig, Extension, Facet, Line, MapMode, Prec, Range, RangeComparator, RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, SpanIterator, StateCommand, StateEffect, StateEffectType, StateField, Text, TextIterator, Transaction, TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };

5

package.json
{
"name": "@codemirror/state",
"version": "0.19.9",
"version": "0.20.0",
"description": "Editor state data structures for the CodeMirror code editor",

@@ -28,5 +28,2 @@ "scripts": {

"license": "MIT",
"dependencies": {
"@codemirror/text": "^0.19.0"
},
"devDependencies": {

@@ -33,0 +30,0 @@ "@codemirror/buildhelper": "^0.1.5"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc