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

apostrophe

Package Overview
Dependencies
Maintainers
1
Versions
1081
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apostrophe - npm Package Compare versions

Comparing version 0.3.14 to 0.3.16

public/blueimp/css/jquery.fileupload-ui-noscript.css

205

apostrophe.js

@@ -11,2 +11,4 @@ var oembed = require('oembed');

var lessMiddleware = require('less-middleware');
var hash_file = require('hash_file');
var path = require('path');

@@ -17,6 +19,6 @@ // MongoDB prefix queries are painful without this

module.exports = function() {
return new apos();
return new aposConstructor();
}
function apos() {
function aposConstructor() {
var self = this;

@@ -32,3 +34,3 @@ var app, files, areas, pages, uploadfs, nunjucksEnv, partial;

self.defaultControls = [ 'style', 'bold', 'italic', 'createLink', 'insertUnorderedList', 'image', 'video', 'pullquote', 'code' ];
self.defaultControls = [ 'style', 'bold', 'italic', 'createLink', 'insertUnorderedList', 'slideshow', 'video', 'pullquote', 'code' ];

@@ -53,3 +55,3 @@ // These are the controls that map directly to standard document.executeCommand

{ value: 'h5', label: 'Heading 5' },
{ value: 'h6', label: 'Heading 6' }
{ value: 'h6', label: 'Heading 6' },
]

@@ -88,11 +90,30 @@ },

self.scripts = [
'/apos/js/jquery-1.8.1.min.js',
// VENDOR DEPENDENCIES
// Makes broken browsers usable
'/apos/js/underscore-min.js',
// For everything
'/apos/js/jquery-1.9.1.min.js',
// For blueimp uploader, drag and drop reordering of anything, datepicker
// & autocomplete
'/apos/jquery-ui-1.10.1/js/jquery-ui-1.10.1.custom.min.js',
// For the RTE
'/apos/js/jquery.hotkeys/jquery.hotkeys.js',
// For selections in the RTE
'/apos/js/rangy-1.2.3/rangy-core.js',
'/apos/js/rangy-1.2.3/rangy-selectionsaverestore.js',
// For selections in ordinary textareas and inputs (part of Rangy)
'/apos/js/textinputs_jquery.js',
'/apos/js/jquery.cookie.js',
// Graceful fallback for older browsers
'/apos/blueimp/js/jquery.iframe-transport.js',
// Spiffy multiple file upload
'/apos/blueimp/js/jquery.fileupload.js',
// OUR CODE
// Editing functionality
'/apos/js/editor.js',
'/apos/js/content.js'
// Viewers for standard content types
'/apos/js/content.js',
];

@@ -104,3 +125,3 @@

self.templates = [
'imageEditor', 'pullquoteEditor', 'videoEditor', 'codeEditor', 'hint'
'slideshowEditor', 'pullquoteEditor', 'videoEditor', 'codeEditor', 'hint'
];

@@ -171,6 +192,11 @@

// Make crucial data such as apos.uploadsUrl available to the browser
aposLocals.aposSetVariables = function() {
return partial('variables.html', { uploadsUrl: uploadfs.getUrl() });
};
// aposTemplates renders templates that are needed on any page that will
// use apos. Examples: imageEditor.html, codeEditor.html, etc. These lie
// dormant in the page until they are needed as prototypes to be cloned
// by jQuery
// use apos. Examples: slideshowEditor.html, codeEditor.html,
// etc. These lie dormant in the page until they are needed as prototypes to
// be cloned by jQuery

@@ -213,2 +239,6 @@ aposLocals.aposTemplates = function(options) {

options.item = item;
if (options.item) {
options.item.position = 'middle';
options.item.size = 'full';
}
return partial('singleton.html', options);

@@ -260,3 +290,3 @@ }

}
attributes[key] = value + '';
attributes[key] = value;
});

@@ -294,2 +324,10 @@ return partial('itemNormalView.html', { item: item, itemType: itemType, options: options, attributes: attributes });

aposLocals.aposFilePath = function(file, options) {
var path = uploadfs.getUrl() + '/files/' + file._id + '-' + file.name;
if (options.size) {
path += '.' + options.size;
}
return path + '.' + file.extension;
}
aposLocals.aposLog = function(m) {

@@ -313,86 +351,48 @@ console.log(m);

// An iframe with file browse and upload buttons.
// We use an iframe because traditional file upload buttons
// can't be AJAXed (although you can do that in Chrome and
// Firefox it is still not supported in IE9).
// Upload a file for slideshow purposes (TODO: extend to
// accept non-image files when appropriate)
app.post('/apos/upload-files', function(req, res) {
var newFiles = req.files.files;
if (!(newFiles instanceof Array)) {
newFiles = [ newFiles ];
}
var infos = [];
async.map(newFiles, function(file, callback) {
var info = {
_id: generateId(),
length: file.length,
createdAt: new Date(),
name: self.slugify(path.basename(file.name, path.extname(file.name)))
};
app.get('/apos/file-iframe/:id', validId, function(req, res) {
var id = req.params.id;
return render(res, 'fileIframe.html', { id: id, error: false, uploaded: false });
});
async.series([ permissions, upload, db ], callback);
// Deliver details about a previously uploaded file as a JSON response
app.get('/apos/file-info/:id', validId, function(req, res) {
var id = req.params.id;
files.findOne({ _id: id }, gotFile);
function gotFile(err, file) {
if (err || (!file)) {
res.statusCode = 404;
res.send("Not Found");
return;
function permissions(callback) {
self.permissions(req, 'edit-media', null, callback);
}
self.permissions(req, 'edit-media', file, function(err) {
if (err) {
return forbid(res);
}
file.url = uploadfs.getUrl() + '/images/' + id;
return res.send(file);
});
}
});
// An upload submitted via the iframe
app.post('/apos/file-iframe/:id', validId, function(req, res) {
var id = req.params.id;
var file = req.files.file;
if (!file) {
return fail(req, res);
}
var src = file.path;
files.findOne({ _id: id }, gotExisting);
var info;
function gotExisting(err, existing) {
self.permissions(req, 'edit-media', existing, function(err) {
if (err) {
return forbid(res);
}
// Let uploadfs do the heavy lifting of scaling and storage to fs or s3
return uploadfs.copyImageIn(src, '/images/' + id, update);
});
}
function update(err, infoArg) {
if (err) {
return fail(req, res);
function upload(callback) {
return uploadfs.copyImageIn(file.path, '/files/' + info._id + '-' + info.name, function(err, result) {
if (err) {
return callback(err);
}
info.extension = result.extension;
return callback(null);
});
}
info = infoArg;
info._id = id;
info.name = self.slugify(file.name);
info.createdAt = new Date();
// Do our best to record who owns this file to allow permissions
// checks later. If req.user exists and has an _id, id or username property,
// record that
if (req.user) {
info.owner = req.user._id || req.user.id || req.user.username;
function db(callback) {
console.log(info);
files.insert(info, { safe: true }, function(err, docs) {
if (!err) {
infos.push(docs[0]);
}
return callback(err);
});
}
files.update({ _id: info._id }, info, { upsert: true, safe: true }, inserted);
}
function inserted(err) {
info.uploaded = true;
info.error = false;
info.id = info._id;
render(res, 'fileIframe.html', info);
}
function fail(req, res) {
return render(res, 'fileIframe.html', { id: id, error: "Not a GIF, JPEG or PNG image file", uploaded: false });
}
}, function(err) {
console.log('result:');
console.log(infos);
return res.send({ files: infos, status: 'ok' });
});
});

@@ -523,2 +523,5 @@

console.log('The item as I received it is:');
console.log(item);
var itemType = self.itemTypes[item.type];

@@ -924,10 +927,11 @@ if (!itemType) {

},
image: {
slideshow: {
widget: true,
label: 'Image',
label: 'Slideshow',
icon: 'image',
// icon: 'slideshow',
render: function(data) {
return partial('image.html', data);
return partial('slideshow.html', data);
},
css: 'image'
css: 'slideshow'
},

@@ -968,2 +972,16 @@ video: {

});
nunjucksEnv.addFilter('jsonAttribute', function(data) {
// Leverage jQuery's willingness to parse attributes as JSON objects and arrays
// if they look like it. TODO: find out if this still works cross browser with
// single quotes, all the double escaping is unfortunate
if (typeof(data) === 'object') {
console.log('it is an object');
return JSON.stringify(data).replace(/\&/g, '&amp;').replace(/\</g, '&lt').replace(/\>/g, '&gt').replace(/\"/g, '&quot;');
} else {
console.log('it is not an object');
// Make it a string for sure
data += '';
return data.replace(/\&/g, '&amp;').replace(/\</g, '&lt').replace(/\>/g, '&gt').replace(/\"/g, '&quot;');
}
});
return nunjucksEnv;

@@ -989,2 +1007,3 @@ }

var regex = new RegExp(r, 'g');
console.log(r);
s = s.replace(regex, '-');

@@ -991,0 +1010,0 @@ // Consecutive dashes become one dash

{
"name": "apostrophe",
"version": "0.3.14",
"version": "0.3.16",
"description": "Apostrophe is a user-friendly content management system. This core module of Apostrophe provides rich content editing and essential facilities to integrate Apostrophe into your Express project. Apostrophe also includes simple facilities for storing your rich content areas in MongoDB and fetching them back again. Additional functionality is available in modules like apostrophe-twitter and apostrophe-rss and forthcoming modules that address page trees, blog posts, events and the like.",

@@ -16,3 +16,4 @@ "main": "apostrophe.js",

"validator": "~0.4.19",
"less-middleware": "~0.1.9"
"less-middleware": "~0.1.9",
"hash_file": "0.0.8"
},

@@ -19,0 +20,0 @@ "devDependencies": {},

@@ -25,3 +25,52 @@ if (!window.apos) {

// An example: the video player replaces a thumbnail with
apos.widgetPlayers.slideshow = function($widget)
{
// TODO take the interval from data attributes, add arrows based on
// data attributes, and so on. Include controls in the form to set
// those options. Consider allowing for titles and descriptions.
// Support all those cool fades we love to do. Et cetera, et cetera.
var interval = setInterval(function() {
var $current = $widget.find('[data-slideshow-item].apos-current');
if (!$current.length) {
// Widget has gone away. Kill the interval timer and go away too
clearInterval(interval);
return;
}
var $next = $current.next();
if (!$next.length) {
$next = $current.closest('[data-slideshow-items]').find('[data-slideshow-item]:first');
}
$current.removeClass('apos-current');
$next.addClass('apos-current');
}, 5000);
// Wait until the height of all images is known, then
// set the height of the entire slideshow. This prevents
// the clearly unacceptable "jumping" behavior.
function adjustSize() {
var ready = true;
var maxHeight = 0;
$widget.find('[data-image]').each(function(i, item) {
if (!item.complete) {
ready = false;
return;
}
var height = $(item).height();
if (height > maxHeight) {
maxHeight = height;
}
});
if (ready) {
$widget.find('[data-slideshow-items]').height(maxHeight);
} else {
setTimeout(adjustSize, 100);
}
}
adjustSize();
}
// The video player replaces a thumbnail with
// a suitable player via apos's oembed proxy

@@ -28,0 +77,0 @@

@@ -91,5 +91,3 @@ if (!window.apos) {

apos.log(styleMenu.length);
styleMenu.find('option').each(function() {
apos.log(this);
styleBlockElements[$(this).val()] = true;

@@ -266,7 +264,5 @@ });

$(node).before(p);
apos.log('prepended prev element');
} else {
var p = node.previousSibling.nodeValue;
if (p.substr(p.length - 1, 1) !== before) {
apos.log('appended prev character');
node.previousSibling.nodeValue += before;

@@ -280,3 +276,2 @@ }

$(node).after(p);
apos.log('appended next element');
} else {

@@ -286,3 +281,2 @@ var n = node.nextSibling.nodeValue;

node.nextSibling.nodeValue = after + node.nextSibling.nodeValue;
apos.log('prepended character to next');
}

@@ -358,3 +352,2 @@ }

self.destroy = function() {
apos.log('destroying editor');
_.map(self.timers, function(timer) { clearInterval(timer); });

@@ -569,3 +562,4 @@ };

var $buttons = $('<div class="apos-widget-buttons"></div>');
var $button = $('<div class="apos-widget-button apos-edit-widget">Edit ' + apos.widgetTypes[$widget.attr('data-type')].label + '</div>');
// var $button = $('<div class="apos-widget-button apos-edit-widget">Edit ' + apos.widgetTypes[$widget.attr('data-type')].label + '</div>');
var $button = $('<div class="apos-widget-button apos-edit-widget"><i class="icon-pencil"></i></div>');
$buttons.append($button);

@@ -600,3 +594,2 @@ var $button = $('<div class="apos-widget-button apos-insert-before-widget">Before</div>');

self.data = options.data;
apos.log(self.data);
}

@@ -621,32 +614,86 @@

// Make our own instance of the editor template
self.$el = $(options.template + '.apos-template').clone();
self.$el.removeClass('.apos-template');
self.$previewContainer = self.$el.find('.apos-widget-preview-container');
if (self.afterCreatingEl) {
self.afterCreatingEl();
}
self.$el.find('[data-preview]').click(function() {
self.preview();
return false;
// Use apos.modalFromTemplate to manage our lifecycle as a modal
self.$el = apos.modalFromTemplate(options.template, {
init: function(callback) {
self.$previewContainer = self.$el.find('.apos-widget-preview-container');
if (self.afterCreatingEl) {
self.afterCreatingEl();
}
self.$el.find('[data-preview]').click(function() {
self.preview();
return false;
});
if (options.fixedPositionAndSize) {
self.$el.find('.apos-position,.apos-size,.apos-lorem').remove();
} else {
self.$el.find('input[type=radio]').change(function() {
self.changeSizeAndPosition();
});
}
var sizeAndPosition = { size: 'two-thirds', position: 'middle' };
if (self.exists) {
sizeAndPosition.size = self.data.size;
sizeAndPosition.position = self.data.position;
}
self.$el.find('input[name="size"]').prop('checked', false);
self.$el.find('input[name="size"][value="' + sizeAndPosition.size + '"]').prop('checked', true);
self.$el.find('input[name="position"]').prop('checked', false);
self.$el.find('input[name="position"][value="' + sizeAndPosition.position + '"]').prop('checked', true);
self.preview();
return callback(null);
},
save: function(callback) {
self.preSave(function() {
if (!self.exists) {
alert(options.messages.missing);
return callback('error');
}
if (self.editor) {
self.editor.undoPoint();
var _new = false;
if (!self.$widget) {
self.createWidget();
_new = true;
}
}
self.updateWidget(function(err) {
if (_new) {
self.insertWidget();
// apos.hint('What are the arrows for?', "<p>They are there to show you where to add other content before and after your rich content.</p><p>Always type text before or after the arrows, never between them.</p><p>This is especially helpful when you are floating content next to text.</p><p>You can click your rich content to select it along with its arrows, then cut, copy or paste as usual.</p><p>Don\'t worry, the arrows automatically disappear when you save your work.</p>");
}
// Widget backups are probably a bad idea since they would defeat
// copy and paste between pages or at least sites
// self.editor.updateWidgetBackup(self.widgetId, self.$widget);
if (options.save) {
// Used to implement save for singletons and non-rich-text-based areas.
// Note that in this case options.data was passed in by reference,
// so the end result can be read there. Pay attention to the callback so
// we can allow the user a second chance
options.save(function(err) {
return callback(err);
});
} else {
return callback(null);
}
});
});
},
afterHide: function(callback) {
self.afterHide();
return callback(null);
}
});
self.$el.find('[data-action="dismiss"]').click(function() {
self.destroy();
});
self.$el.find('input[type=radio]').change(function() {
self.changeSizeAndPosition();
});
// Default methods
_.defaults(self, {
destroy: function() {
self.modal('hide');
afterHide: function() {
_.map(self.timers, function(timer) { clearInterval(timer); });
// Let it go away pretty, then remove it from the DOM
setTimeout(function() {
self.$el.remove();
}, 500);
// Return focus to the main editor
if (self.editor) {
self.editor.$editable.focus();
}
},

@@ -738,4 +785,3 @@

_.each(self.data, function(val, key) {
apos.log(key + ': ' + val);
self.$widget.attr('data-' + key, val);
self.$widget.attr('data-' + key, apos.jsonAttribute(val));
});

@@ -751,2 +797,4 @@ },

var info = self.$widget.data();
console.log('rendering these attributes:');
console.log(info);

@@ -803,9 +851,2 @@ // Some widgets have content - markup that goes inside the widget

modal: function(command) {
// TODO: this should utilize all the good stuff in
// apos.modal, not just apos._modalCore, I need to
// refactor out the redundancies
return apos._modalCore(self.$el, command);
},
preview: function() {

@@ -851,71 +892,14 @@ if (self.prePreview) {

}
}
});
},
self.$el.find('.apos-save').click(function() {
self.preSave(function() {
if (!self.exists) {
alert(options.messages.missing);
return false;
}
if (self.editor) {
self.editor.undoPoint();
var _new = false;
if (!self.$widget) {
self.createWidget();
_new = true;
}
}
self.updateWidget(function(err) {
if (_new) {
self.insertWidget();
// apos.hint('What are the arrows for?', "<p>They are there to show you where to add other content before and after your rich content.</p><p>Always type text before or after the arrows, never between them.</p><p>This is especially helpful when you are floating content next to text.</p><p>You can click your rich content to select it along with its arrows, then cut, copy or paste as usual.</p><p>Don\'t worry, the arrows automatically disappear when you save your work.</p>");
}
// Widget backups are probably a bad idea since they would defeat
// copy and paste between pages or at least sites
// self.editor.updateWidgetBackup(self.widgetId, self.$widget);
if (options.save) {
// Used to implement save for singletons and non-rich-text-based areas.
// Note that in this case options.data was passed in by reference,
// so the end result can be read there. Pay attention to the callback so
// we can allow the user a second chance
options.save(function(err) {
if (!err) {
self.destroy();
}
});
} else {
self.destroy();
}
});
});
return false;
});
// Override if you need to carry out an action such
// as fetching video information before the save can
// take place. Takes a callback which completes the
// save operation, or gracefully refuses it if you
// don't set self.exists to true. Use of a callback
// allows you to asynchronously fetch video information, etc.
if (!self.preSave) {
self.preSave = function(callback) {
// Override if you need to carry out an action such
// as fetching video information before the save can
// take place. Takes a callback which completes the
// save operation, or gracefully refuses it if you
// don't set self.exists to true. Use of a callback
// allows you to asynchronously fetch video information, etc.
preSave: function(callback) {
callback();
}
}
var sizeAndPosition = { size: 'two-thirds', position: 'middle' };
if (self.exists) {
sizeAndPosition.size = self.data.size;
sizeAndPosition.position = self.data.position;
}
self.$el.find('input[name="size"]').prop('checked', false);
self.$el.find('input[name="size"][value="' + sizeAndPosition.size + '"]').prop('checked', true);
self.$el.find('input[name="position"]').prop('checked', false);
self.$el.find('input[name="position"][value="' + sizeAndPosition.position + '"]').prop('checked', true);
self.preview();
self.modal();
});
}

@@ -925,6 +909,7 @@

apos.widgetTypes.image = {
label: 'Image',
apos.widgetTypes.slideshow = {
label: 'Slideshow',
editor: function(options) {
var self = this;
var $items;

@@ -939,31 +924,90 @@ if (!options.messages) {

self.afterCreatingEl = function() {
self.$el.find('[data-iframe-placeholder]').replaceWith($('<iframe id="iframe-' + self.data.id + '" name="iframe-' + self.data.id + '" class="apos-file-iframe" src="/apos/file-iframe/' + self.data.id + '"></iframe>'));
self.$el.bind('uploaded', function(e, id) {
// Only react to events intended for us
if (id === self.data.id) {
self.exists = true;
$items = self.$el.find('[data-items]');
$items.sortable({
update: function(event, ui) {
reflect();
self.preview();
}
});
self.files = [];
self.$el.find('[data-uploader]').fileupload({
dataType: 'json',
// This is nice in a multiuser scenario, it prevents slamming,
// but I need to figure out why it's necessary to avoid issues
// with node-imagemagick
sequentialUploads: true,
start: function (e) {
$('[data-progress]').show();
$('[data-finished]').hide();
},
stop: function (e) {
$('[data-progress]').hide();
$('[data-finished]').show();
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
self.$el.find('[data-progress-percentage]').text(progress);
},
done: function (e, data) {
if (data.result.files) {
_.each(data.result.files, function (file) {
addItem(file);
});
reflect();
self.preview();
}
}
});
};
// For images, preview is triggered by an upload. We need to capture
// the file extension at that point so we are not forced to constantly
// look it up in a separate collection
// The server will render an actual slideshow, but we also want to see
// thumbnails of everything with draggability for reordering and remove buttons
self.prePreview = function(callback) {
if (self.exists) {
$.getJSON('/apos/file-info/' + self.data.id, function(info) {
self.data.extension = info.extension;
callback();
});
console.log('self.data in prePreview');
console.log(self.data);
$items.find('[data-item]:not(.apos-template)').remove();
var items = self.data.items;
if (!items) {
items = [];
}
else
{
callback();
}
_.each(items, function(item) {
addItem(item);
});
callback();
};
self.type = 'image';
options.template = '.apos-image-editor';
function addItem(item) {
var $item = $items.find('[data-item].apos-template').clone();
$item.removeClass('apos-template');
$item.find('[data-image]').attr('src', apos.uploadsUrl + '/files/' + item._id + '-' + item.name + '.one-third.' + item.extension);
$item.data('item', item);
$item.click('[data-remove]', function() {
$item.remove();
reflect();
self.preview();
return false;
});
$items.append($item);
}
// Update the data attributes to match what is found in the
// list of items. This is called after remove and reorder events
function reflect() {
$itemElements = $items.find('[data-item]:not(.apos-template)');
self.data.items = [];
$.each($itemElements, function(i, item) {
var $item = $(item);
self.data.items.push($item.data('item'));
});
// An empty slideshow is allowed, so permit it to be saved
// even if nothing has been added
self.exists = true;
}
self.type = 'slideshow';
options.template = '.apos-slideshow-editor';
// Parent class constructor shared by all widget editors

@@ -992,3 +1036,3 @@ apos.widgetEditor.call(self, options);

// fresh paste
var last = '';
var last = self.data.video;
self.timers.push(setInterval(function() {

@@ -1263,3 +1307,3 @@ var next = self.$embed.val();

var editView = $('<div class="apos-edit-view"></div>');
editView.append($(data));
editView.append(data);
area.append(editView);

@@ -1305,3 +1349,3 @@ area.find('.apos-normal-view').hide();

var itemData = {};
var itemData = { position: 'middle', size: 'full' };
var $item = $singleton.find('.apos-content .apos-widget :first');

@@ -1312,5 +1356,5 @@ if ($item.length) {

new apos.widgetTypes[type].editor({
fixedPositionAndSize: true,
data: itemData,
save: function(callback) {
apos.log(itemData);
$.post('/apos/edit-singleton',

@@ -1359,14 +1403,6 @@ {

item.content = apos.widgetTypes[type].getContent($(child));
apos.log(item.content);
}
for (var j = 0; (j < child.attributes.length); j++) {
var key = child.attributes[j].name;
var value = child.attributes[j].value;
var matches = key.match(/^data\-(.*)$/);
if (matches) {
var name = matches[1];
item[name] = value;
}
}
var data = $(child).data();
_.extend(item, data);
items.push(item);

@@ -1412,5 +1448,5 @@ } else {

var blackout = $('<div class="apos-modal-blackout"></div>');
$('body').append(blackout);
$el.offset({ top: $('body').scrollTop() + 200, left: ($(window).width() - 600) / 2 });
$('body').append(blackout);
$('body').append($el);
$el.offset({ top: $('body').scrollTop() + 100, left: ($(window).width() - $el.outerWidth()) / 2 });
$el.show();

@@ -1478,3 +1514,2 @@ }

$el.find('.apos-save').click();
apos.log('triggered save');
return false;

@@ -1640,1 +1675,15 @@ });

}
// For use when storing DOM attributes meant to be compatible
// with jQuery's built-in support for JSON parsing of
// objects and arrays in data attributes. We'll be passing
// the result to .attr, so we don't have to worry about
// HTML escaping.
apos.jsonAttribute = function(value) {
if (typeof(value) === 'object') {
return JSON.stringify(value);
} else {
return value;
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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