Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@neocodemirror/svelte

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@neocodemirror/svelte - npm Package Compare versions

Comparing version 0.0.15 to 0.0.16

125

./dist/index.js

@@ -1,2 +0,2 @@

import { defaultKeymap, indentWithTab } from '@codemirror/commands';
import { historyField, defaultKeymap, indentWithTab } from '@codemirror/commands';
import { indentUnit } from '@codemirror/language';

@@ -11,8 +11,10 @@ import { Compartment, EditorState } from '@codemirror/state';

extensions: null,
value: null
value: null,
documents: /* @__PURE__ */ new Map()
});
var codemirror = (node, options) => {
if (is_undefined(options))
if (is_nullish(options))
throw new Error("No options provided. At least `value` is required.");
let { value, instanceStore, onChangeBehavior = { kind: "debounce", duration: 50 } } = options;
const EDITOR_STATE_MAP = /* @__PURE__ */ new Map();
let fulfill_editor_initialized;

@@ -38,3 +40,3 @@ let editor_initialized = new Promise((r) => fulfill_editor_initialized = r);

autocomplete_compartment.of(await get_autocompletion(options2)),
setup_compartment.of(await get_setup(options2) ?? []),
setup_compartment.of(await get_setup(options2)),
// Needs to be under `setup` because setup, if there, will add the indentWithTab

@@ -49,2 +51,5 @@ keymap.of([...defaultKeymap, ...options2.useTabs ? [indentWithTab] : []]),

}
function dispatch_event(event, detail) {
node.dispatchEvent(new CustomEvent(event, detail ? { detail } : void 0));
}
function handle_change(view_update) {

@@ -54,8 +59,15 @@ const new_value = view.state.doc.toString();

value = new_value;
node.dispatchEvent(new CustomEvent("codemirror:textChange", { detail: value }));
dispatch_event("codemirror:textChange", value);
}
instanceStore?.set({ value, view, extensions: internal_extensions });
node.dispatchEvent(new CustomEvent("codemirror:change", { detail: view_update }));
instanceStore?.set({
value,
view,
extensions: internal_extensions,
documents: EDITOR_STATE_MAP
});
dispatch_event("codemirror:change", view_update);
}
const { kind: behaviorKind = "debounce", duration: behaviorDuration = 50 } = onChangeBehavior;
const { kind: behaviorKind = "debounce", duration: behaviorDuration = 50 } = is_nullish(
onChangeBehavior
) ? {} : onChangeBehavior;
let on_change = behaviorKind === "debounce" ? debounce(handle_change, behaviorDuration) : throttle(handle_change, behaviorDuration);

@@ -76,3 +88,3 @@ (async () => {

});
if (!is_undefined(options.cursorPos))
if (!is_nullish(options.cursorPos))
view.focus();

@@ -86,10 +98,10 @@ fulfill_editor_initialized();

if (!is_equal(value, new_options.value)) {
value = new_options.value;
transaction.changes = {
from: 0,
to: view.state.doc.length,
insert: value
insert: new_options.value
};
}
if (!is_undefined(new_options.cursorPos) && !is_equal(options.cursorPos, new_options.cursorPos)) {
if (!is_nullish(new_options.cursorPos) && !is_equal(options.cursorPos, new_options.cursorPos)) {
console.log("setting cursorPos", new_options.cursorPos);
transaction.selection = {

@@ -104,8 +116,8 @@ anchor: new_options.cursorPos ?? 0,

const effects = transaction.effects;
let are_all_options_undefined = true;
let are_all_options_nullish = true;
for (const option_name of options_list) {
const new_option = new_options[option_name];
const old_option = options[option_name];
if (!is_undefined(new_option)) {
are_all_options_undefined = false;
if (!is_nullish(new_option)) {
are_all_options_nullish = false;
if (!is_equal(new_option, old_option)) {

@@ -116,18 +128,46 @@ return effects.push(compartment.reconfigure(await factory(new_options)));

}
if (are_all_options_undefined)
if (are_all_options_nullish)
effects.push(compartment.reconfigure([]));
}
await Promise.all([
append_effect(setup_compartment, ["setup"], get_setup),
append_effect(lang_compartment, ["lang"], get_lang),
append_effect(tabs_compartment, ["useTabs", "tabSize"], get_tab_setting),
append_effect(theming_compartment, ["theme"], get_theme),
append_effect(extensions_compartment, ["extensions"], get_user_extensions),
append_effect(readonly_compartment, ["readonly"], get_readonly),
append_effect(autocomplete_compartment, ["autocomplete"], get_autocompletion),
append_effect(linter_compartment, ["lint", "lintOptions"], get_linter)
]);
view.dispatch(transaction);
const pre_transaction_state = view.state.toJSON(
is_object(new_options.documentFields) ? new_options.documentFields : DEFAULT_DOCUMENT_FIELDS
);
const internal_extensions_promise = make_extensions(new_options);
if (is_equal(options.documentId, new_options.documentId) && !is_nullish(options.documentId)) {
console.log(1);
await Promise.all([
append_effect(setup_compartment, ["setup"], get_setup),
append_effect(lang_compartment, ["lang"], get_lang),
append_effect(tabs_compartment, ["useTabs", "tabSize"], get_tab_setting),
append_effect(theming_compartment, ["theme"], get_theme),
append_effect(extensions_compartment, ["extensions"], get_user_extensions),
append_effect(readonly_compartment, ["readonly"], get_readonly),
append_effect(autocomplete_compartment, ["autocomplete"], get_autocompletion),
append_effect(linter_compartment, ["lint", "lintOptions"], get_linter)
]);
view.dispatch(transaction);
internal_extensions = await internal_extensions_promise;
}
if (!is_nullish(options.documentId) && !is_equal(options.documentId, new_options.documentId)) {
EDITOR_STATE_MAP.set(options.documentId, pre_transaction_state);
if (!is_nullish(new_options.documentId)) {
const old_state = EDITOR_STATE_MAP.get(new_options.documentId);
dispatch_event("codemirror:documentChanging", { view });
internal_extensions = await internal_extensions_promise;
view.setState(
old_state ? EditorState.fromJSON(
old_state,
{ extensions: internal_extensions, doc: new_options.value },
is_object(new_options.documentFields) ? new_options.documentFields : DEFAULT_DOCUMENT_FIELDS
) : EditorState.create({
doc: new_options.value,
extensions: internal_extensions
})
);
view.focus();
dispatch_event("codemirror:documentChanged", { view });
}
}
const { kind: behaviorKind2 = "debounce", duration: behaviorDuration2 = 50 } = new_options.onChangeBehavior ?? { kind: "debounce", duration: 50 };
if (!is_equal(options.onChangeBehavior?.kind, behaviorKind2) || !is_equal(options.onChangeBehavior?.duration, behaviorDuration2)) {
if (!is_equal(options.onChangeBehavior, new_options.onChangeBehavior)) {
on_change = behaviorKind2 === "debounce" ? debounce(handle_change, behaviorDuration2) : throttle(handle_change, behaviorDuration2);

@@ -143,5 +183,8 @@ }

};
var DEFAULT_DOCUMENT_FIELDS = {
history: historyField
};
async function get_setup(options) {
const { setup } = options;
if (is_undefined(setup))
if (is_nullish(setup))
return [];

@@ -157,3 +200,3 @@ if (setup === "basic")

async function get_lang({ lang, langMap }) {
if (is_undefined(lang))
if (is_nullish(lang))
return [];

@@ -174,6 +217,8 @@ if (typeof lang === "string") {

async function get_tab_setting({ useTabs = false, tabSize = 2 }) {
if (is_nullish(tabSize))
return [];
return [EditorState.tabSize.of(tabSize), indentUnit.of(useTabs ? " " : " ".repeat(tabSize))];
}
async function get_autocompletion({ autocomplete }) {
if (is_undefined(autocomplete))
if (is_nullish(autocomplete))
return [];

@@ -190,3 +235,3 @@ const { autocompletion } = await import('@codemirror/autocomplete');

async function get_linter({ lint, lintOptions = {} }) {
if (is_undefined(lint))
if (is_nullish(lint))
return [];

@@ -196,7 +241,17 @@ if (!is_function(lint))

const { linter } = await import('@codemirror/lint');
return linter(lint, lintOptions);
return linter(lint, lintOptions ?? {});
}
var is_equal = (a, b) => a === b;
var is_undefined = (a) => typeof a === "undefined";
var is_equal = (a, b) => {
if (!is_object(a) || !is_object(b))
return a === b;
const sortedStr = (obj) => JSON.stringify(obj, Object.keys(obj).sort());
try {
return sortedStr(a) === sortedStr(b);
} catch {
return false;
}
};
var is_nullish = (a) => a == null;
var is_function = (a) => typeof a === "function";
var is_object = (a) => !is_nullish(a) && typeof a === "object";
function debounce(func, threshold, execAsap = false) {

@@ -203,0 +258,0 @@ let timeout;

import * as _codemirror_autocomplete from '@codemirror/autocomplete';
import { LanguageSupport } from '@codemirror/language';
import { LintSource, linter } from '@codemirror/lint';
import { Extension, Transaction } from '@codemirror/state';
import { Extension, StateField, Transaction } from '@codemirror/state';
import { EditorView } from '@codemirror/view';

@@ -75,3 +75,3 @@ import { Properties } from 'csstype';

*/
setup?: 'basic' | 'minimal';
setup?: 'basic' | 'minimal' | null;
/**

@@ -114,3 +114,3 @@ * The language to use. Can be either a `LanguageSupport` or a string.

*/
lang?: LanguageSupport | string;
lang?: LanguageSupport | string | null;
/**

@@ -137,3 +137,3 @@ * A map of language names to functions that return a `LanguageSupport`. Can be promises too.

*/
langMap?: Record<string, () => MaybePromise<LanguageSupport>>;
langMap?: Record<string, () => MaybePromise<LanguageSupport>> | null;
/**

@@ -151,3 +151,3 @@ * Whether to use tabs or spaces. Defaults to spaces.

*/
useTabs?: boolean;
useTabs?: boolean | null;
/**

@@ -165,3 +165,3 @@ * The size of a tab in spaces. Defaults to 2.

*/
tabSize?: number;
tabSize?: number | null;
/**

@@ -180,3 +180,3 @@ * Whether to open the editor in readonly mode. Note its different from `editable`, which allows you to focus cursor in editor, but not make any changes.

*/
readonly?: boolean;
readonly?: boolean | null;
/**

@@ -192,3 +192,3 @@ * Cursor Position. If not specified, defaults to the start of the document.

*/
cursorPos?: number;
cursorPos?: number | null;
/**

@@ -204,3 +204,3 @@ * Whether to autocomplete the language's basics

*/
autocomplete?: boolean | Parameters<typeof _codemirror_autocomplete.autocompletion>[0];
autocomplete?: boolean | Parameters<typeof _codemirror_autocomplete.autocompletion>[0] | null;
/**

@@ -218,3 +218,3 @@ * Styles to pass to EditorView.theme. Defaults to none.

*/
styles?: Styles;
styles?: Styles | null;
/**

@@ -234,3 +234,3 @@ * The theme to use. Of type `Extension`. Defaults to none.

*/
theme?: Extension;
theme?: Extension | null;
/**

@@ -265,3 +265,3 @@ * A (possibly async) function to provide diagnostic hints for your code(squiggles for error, warning, info, etc).

*/
lint?: LintSource;
lint?: LintSource | null;
/**

@@ -287,3 +287,3 @@ * Options to pass to the linter. Defaults to none.

*/
lintOptions?: Parameters<typeof linter>[1];
lintOptions?: Parameters<typeof linter>[1] | null;
/**

@@ -304,3 +304,3 @@ * The extensions to use. Defaults to empty array.

*/
extensions?: Extension[];
extensions?: Extension[] | null;
/**

@@ -324,3 +324,3 @@ * Instance store passed to the editor. This is created with `withCodemirrorInstance` function. It lets you track any and all state changes.

*/
instanceStore?: MapStore<CodemirrorInstance>;
instanceStore?: MapStore<CodemirrorInstance> | null;
/**

@@ -341,3 +341,33 @@ * Options to config the behavior of the onChange/onTextChange callback. You can specify a kind

duration?: number;
};
} | null;
/**
* If present it will make the codemirror instance enter document mode. This means that whenever
* the documentId changes the state of the codemirror instance is reset and stored in a map.
* If there's a stored state for the new documentId it will be restored. This allows, for example
* to keep different undo-redo history for different documents.
*
* @default undefined
*
* @example
* ```svelte
* <div use:codemirror={{ documentId: "file.txt" />
* ```
*/
documentId?: string | null;
/**
* Fields to pass to codemirror while saving state in document mode. By default saves history.
*
* @default { history: historyField }
*
* @example
*
* ```svelte
* <script>
* import { historyField } from '@codemirror/commands';
* </script>
*
* <div use:codemirror={{ documentId: "file.txt", documentFields: { history: historyField } }} />
* ```
*/
documentFields?: Record<string, StateField<any>> | null;
};

@@ -348,2 +378,3 @@ type CodemirrorInstance = {

value: string | null;
documents: Map<string, string>;
};

@@ -354,4 +385,10 @@ declare const withCodemirrorInstance: () => MapStore<CodemirrorInstance>;

'on:codemirror:change'?: ((e: CustomEvent<Transaction>) => void) | undefined;
'on:codemirror:documentChanging'?: ((e: CustomEvent<{
view: EditorView;
}>) => void) | undefined;
'on:codemirror:documentChanged'?: ((e: CustomEvent<{
view: EditorView;
}>) => void) | undefined;
}>;
export { NeoCodemirrorOptions, codemirror, withCodemirrorInstance };

@@ -1,2 +0,2 @@

import { defaultKeymap, indentWithTab } from '@codemirror/commands';
import { historyField, defaultKeymap, indentWithTab } from '@codemirror/commands';
import { indentUnit } from '@codemirror/language';

@@ -11,8 +11,10 @@ import { Compartment, EditorState } from '@codemirror/state';

extensions: null,
value: null
value: null,
documents: /* @__PURE__ */ new Map()
});
var codemirror = (node, options) => {
if (is_undefined(options))
if (is_nullish(options))
throw new Error("No options provided. At least `value` is required.");
let { value, instanceStore, onChangeBehavior = { kind: "debounce", duration: 50 } } = options;
const EDITOR_STATE_MAP = /* @__PURE__ */ new Map();
let fulfill_editor_initialized;

@@ -38,3 +40,3 @@ let editor_initialized = new Promise((r) => fulfill_editor_initialized = r);

autocomplete_compartment.of(await get_autocompletion(options2)),
setup_compartment.of(await get_setup(options2) ?? []),
setup_compartment.of(await get_setup(options2)),
// Needs to be under `setup` because setup, if there, will add the indentWithTab

@@ -49,2 +51,5 @@ keymap.of([...defaultKeymap, ...options2.useTabs ? [indentWithTab] : []]),

}
function dispatch_event(event, detail) {
node.dispatchEvent(new CustomEvent(event, detail ? { detail } : void 0));
}
function handle_change(view_update) {

@@ -54,8 +59,15 @@ const new_value = view.state.doc.toString();

value = new_value;
node.dispatchEvent(new CustomEvent("codemirror:textChange", { detail: value }));
dispatch_event("codemirror:textChange", value);
}
instanceStore?.set({ value, view, extensions: internal_extensions });
node.dispatchEvent(new CustomEvent("codemirror:change", { detail: view_update }));
instanceStore?.set({
value,
view,
extensions: internal_extensions,
documents: EDITOR_STATE_MAP
});
dispatch_event("codemirror:change", view_update);
}
const { kind: behaviorKind = "debounce", duration: behaviorDuration = 50 } = onChangeBehavior;
const { kind: behaviorKind = "debounce", duration: behaviorDuration = 50 } = is_nullish(
onChangeBehavior
) ? {} : onChangeBehavior;
let on_change = behaviorKind === "debounce" ? debounce(handle_change, behaviorDuration) : throttle(handle_change, behaviorDuration);

@@ -76,3 +88,3 @@ (async () => {

});
if (!is_undefined(options.cursorPos))
if (!is_nullish(options.cursorPos))
view.focus();

@@ -86,10 +98,10 @@ fulfill_editor_initialized();

if (!is_equal(value, new_options.value)) {
value = new_options.value;
transaction.changes = {
from: 0,
to: view.state.doc.length,
insert: value
insert: new_options.value
};
}
if (!is_undefined(new_options.cursorPos) && !is_equal(options.cursorPos, new_options.cursorPos)) {
if (!is_nullish(new_options.cursorPos) && !is_equal(options.cursorPos, new_options.cursorPos)) {
console.log("setting cursorPos", new_options.cursorPos);
transaction.selection = {

@@ -104,8 +116,8 @@ anchor: new_options.cursorPos ?? 0,

const effects = transaction.effects;
let are_all_options_undefined = true;
let are_all_options_nullish = true;
for (const option_name of options_list) {
const new_option = new_options[option_name];
const old_option = options[option_name];
if (!is_undefined(new_option)) {
are_all_options_undefined = false;
if (!is_nullish(new_option)) {
are_all_options_nullish = false;
if (!is_equal(new_option, old_option)) {

@@ -116,18 +128,46 @@ return effects.push(compartment.reconfigure(await factory(new_options)));

}
if (are_all_options_undefined)
if (are_all_options_nullish)
effects.push(compartment.reconfigure([]));
}
await Promise.all([
append_effect(setup_compartment, ["setup"], get_setup),
append_effect(lang_compartment, ["lang"], get_lang),
append_effect(tabs_compartment, ["useTabs", "tabSize"], get_tab_setting),
append_effect(theming_compartment, ["theme"], get_theme),
append_effect(extensions_compartment, ["extensions"], get_user_extensions),
append_effect(readonly_compartment, ["readonly"], get_readonly),
append_effect(autocomplete_compartment, ["autocomplete"], get_autocompletion),
append_effect(linter_compartment, ["lint", "lintOptions"], get_linter)
]);
view.dispatch(transaction);
const pre_transaction_state = view.state.toJSON(
is_object(new_options.documentFields) ? new_options.documentFields : DEFAULT_DOCUMENT_FIELDS
);
const internal_extensions_promise = make_extensions(new_options);
if (is_equal(options.documentId, new_options.documentId) && !is_nullish(options.documentId)) {
console.log(1);
await Promise.all([
append_effect(setup_compartment, ["setup"], get_setup),
append_effect(lang_compartment, ["lang"], get_lang),
append_effect(tabs_compartment, ["useTabs", "tabSize"], get_tab_setting),
append_effect(theming_compartment, ["theme"], get_theme),
append_effect(extensions_compartment, ["extensions"], get_user_extensions),
append_effect(readonly_compartment, ["readonly"], get_readonly),
append_effect(autocomplete_compartment, ["autocomplete"], get_autocompletion),
append_effect(linter_compartment, ["lint", "lintOptions"], get_linter)
]);
view.dispatch(transaction);
internal_extensions = await internal_extensions_promise;
}
if (!is_nullish(options.documentId) && !is_equal(options.documentId, new_options.documentId)) {
EDITOR_STATE_MAP.set(options.documentId, pre_transaction_state);
if (!is_nullish(new_options.documentId)) {
const old_state = EDITOR_STATE_MAP.get(new_options.documentId);
dispatch_event("codemirror:documentChanging", { view });
internal_extensions = await internal_extensions_promise;
view.setState(
old_state ? EditorState.fromJSON(
old_state,
{ extensions: internal_extensions, doc: new_options.value },
is_object(new_options.documentFields) ? new_options.documentFields : DEFAULT_DOCUMENT_FIELDS
) : EditorState.create({
doc: new_options.value,
extensions: internal_extensions
})
);
view.focus();
dispatch_event("codemirror:documentChanged", { view });
}
}
const { kind: behaviorKind2 = "debounce", duration: behaviorDuration2 = 50 } = new_options.onChangeBehavior ?? { kind: "debounce", duration: 50 };
if (!is_equal(options.onChangeBehavior?.kind, behaviorKind2) || !is_equal(options.onChangeBehavior?.duration, behaviorDuration2)) {
if (!is_equal(options.onChangeBehavior, new_options.onChangeBehavior)) {
on_change = behaviorKind2 === "debounce" ? debounce(handle_change, behaviorDuration2) : throttle(handle_change, behaviorDuration2);

@@ -143,5 +183,8 @@ }

};
var DEFAULT_DOCUMENT_FIELDS = {
history: historyField
};
async function get_setup(options) {
const { setup } = options;
if (is_undefined(setup))
if (is_nullish(setup))
return [];

@@ -157,3 +200,3 @@ if (setup === "basic")

async function get_lang({ lang, langMap }) {
if (is_undefined(lang))
if (is_nullish(lang))
return [];

@@ -174,6 +217,8 @@ if (typeof lang === "string") {

async function get_tab_setting({ useTabs = false, tabSize = 2 }) {
if (is_nullish(tabSize))
return [];
return [EditorState.tabSize.of(tabSize), indentUnit.of(useTabs ? " " : " ".repeat(tabSize))];
}
async function get_autocompletion({ autocomplete }) {
if (is_undefined(autocomplete))
if (is_nullish(autocomplete))
return [];

@@ -190,3 +235,3 @@ const { autocompletion } = await import('@codemirror/autocomplete');

async function get_linter({ lint, lintOptions = {} }) {
if (is_undefined(lint))
if (is_nullish(lint))
return [];

@@ -196,7 +241,17 @@ if (!is_function(lint))

const { linter } = await import('@codemirror/lint');
return linter(lint, lintOptions);
return linter(lint, lintOptions ?? {});
}
var is_equal = (a, b) => a === b;
var is_undefined = (a) => typeof a === "undefined";
var is_equal = (a, b) => {
if (!is_object(a) || !is_object(b))
return a === b;
const sortedStr = (obj) => JSON.stringify(obj, Object.keys(obj).sort());
try {
return sortedStr(a) === sortedStr(b);
} catch {
return false;
}
};
var is_nullish = (a) => a == null;
var is_function = (a) => typeof a === "function";
var is_object = (a) => !is_nullish(a) && typeof a === "object";
function debounce(func, threshold, execAsap = false) {

@@ -203,0 +258,0 @@ let timeout;

{
"name": "@neocodemirror/svelte",
"version": "0.0.15",
"version": "0.0.16",
"description": "Svelte Action to add codemirro to your apps 😉",

@@ -44,15 +44,15 @@ "main": "./dist/index.js",

"csstype": "^3.1.2",
"nanostores": "^0.8.1"
"nanostores": "^0.9.3"
},
"devDependencies": {
"svelte": "^3.58.0"
"svelte": "^3.58.0 || ^4.0.0"
},
"peerDependencies": {
"@codemirror/autocomplete": "^6.7.1",
"@codemirror/autocomplete": "^6.8.1",
"@codemirror/commands": "^6.2.4",
"@codemirror/language": "^6.7.0",
"@codemirror/lint": "^6.2.1",
"@codemirror/search": "^6.4.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.12.0"
"@codemirror/language": "^6.8.0",
"@codemirror/lint": "^6.4.0",
"@codemirror/search": "^6.5.0",
"@codemirror/state": "^6.2.1",
"@codemirror/view": "^6.14.0"
},

@@ -59,0 +59,0 @@ "homepage": "https://github.com/PuruVJ/neocodemirror",

@@ -43,1 +43,39 @@ Neocodemirror

Note: Passing the store recieved from `withCodemirrorInstance` is required to get the editor related data. If you don't pass this store, you will not get any data.
## Document mode
If you pass a `documentId` in the options you'll automatically enter document mode. In this mode whenever the `documentId` changes the state of the editor get's stored in a map and will later be restored when the `documentId` changes again. This allows for the history to be `documentId` contained (so for example if you change documentId and try to Ctrl+Z or Cmd+Z it will not work). Right before this swap and right after two events `on:codemirror:documentChanging` and `on:codemirror:documentChanged` will be fired. This allows you to store additional state that might not be serializable in the codemirror state.
```svelte
<script>
import { codemirror } from '@neocodemirror/svelte'
import { javascript } from '@codemirror/lang-javascript'
const documents = [
{
title: '+page.svelte',
content: '<scri lang="ts">export let data</scri'++'pt> {data.name}'
},
{
title: '+page.js',
content: 'export function load(){ return {name: "neocodemirror"} }'
},
];
let selected_document = 0;
</script>
{#each documents as document, i}
<button on:click={()=> selected_document=i}>{document.title}</button>
{/each}
<div
on:codemirror:changeText={(new_text)=>{
documents[selected_document].content=new_text;
}}
use:codemirror={{
value: documents[selected_document].content,
documentId: documents[selected_document].title
}}
/>
```
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