Socket
Socket
Sign inDemoInstall

service-worker-mock

Package Overview
Dependencies
5
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.12.0 to 2.0.0

models/BroadcastChannel.js

9

fetch.js

@@ -1,1 +0,8 @@

module.exports = () => ({ fetch: Promise.resolve() });
module.exports = async (request) => {
const response = new Response('Response from service-worker-mock/fetch.js', {
status: 200,
statusText: 'ok.'
});
response.url = request.url;
return response;
};

84

index.js

@@ -5,2 +5,10 @@ // If the WHATWG URL implementation is available via the first-party `url`

const URL = require('url').URL || require('dom-urls');
const {
IDBFactory,
IDBKeyRange,
IDBDatabase,
IDBObjectStore,
reset: resetIDB
} = require('shelving-mock-indexeddb');
const { Performance } = require('w3c-hr-time');

@@ -14,5 +22,12 @@ const Blob = require('./models/Blob');

const Clients = require('./models/Clients');
const DOMException = require('./models/DOMException');
const ExtendableEvent = require('./models/ExtendableEvent');
const ExtendableMessageEvent = require('./models/ExtendableMessageEvent');
const Event = require('./models/Event');
const EventTarget = require('./models/EventTarget');
const FetchEvent = require('./models/FetchEvent');
const Headers = require('./models/Headers');
const MessageEvent = require('./models/MessageEvent');
const MessageChannel = require('./models/MessageChannel');
const MessagePort = require('./models/MessagePort');
const Notification = require('./models/Notification');

@@ -26,3 +41,6 @@ const NotificationEvent = require('./models/NotificationEvent');

const ServiceWorkerRegistration = require('./models/ServiceWorkerRegistration');
const MessageEvent = require('./models/MessageEvent');
const SyncEvent = require('./models/SyncEvent');
const URLSearchParams = require('url-search-params');
const BroadcastChannel = require('./models/BroadcastChannel');
const FileReader = require('./models/FileReader');

@@ -32,12 +50,11 @@ const eventHandler = require('./utils/eventHandler');

const defaults = (envOptions) => Object.assign({
locationUrl: 'https://www.test.com'
locationUrl: 'https://www.test.com/sw.js',
userAgent: 'Mock User Agent',
useRawRequestUrl: false
}, envOptions);
const makeListenersWithReset = () => {
const listeners = {};
const makeListenersWithReset = (listeners, resetEventListeners) => {
Object.defineProperty(listeners, 'reset', {
enumerable: false,
value: () => {
self.listeners = makeListenersWithReset();
}
value: resetEventListeners
});

@@ -47,6 +64,15 @@ return listeners;

class ServiceWorkerGlobalScope {
class ServiceWorkerGlobalScope extends EventTarget {
constructor(envOptions) {
super();
const options = defaults(envOptions);
this.listeners = makeListenersWithReset();
// For backwards compatibility, resetting global scope listeners
// will reset ExtenableEvents as well
this.listeners = makeListenersWithReset(this.listeners, () => {
this.resetEventListeners();
ExtendableEvent._allExtendableEvents.clear();
});
this.useRawRequestUrl = options.useRawRequestUrl;
this.location = new URL(options.locationUrl, options.locationBase);

@@ -61,9 +87,22 @@ this.skipWaiting = () => Promise.resolve();

this.Body = Body;
this.BroadcastChannel = BroadcastChannel;
this.Cache = Cache;
this.Client = Client;
this.WindowClient = WindowClient;
this.Event = ExtendableEvent;
this.DOMException = DOMException;
this.Event = Event;
this.EventTarget = EventTarget;
this.ExtendableEvent = ExtendableEvent;
this.ExtendableMessageEvent = ExtendableMessageEvent;
this.FetchEvent = FetchEvent;
this.FileReader = FileReader;
this.Headers = Headers;
this.importScripts = () => {};
this.indexedDB = new IDBFactory();
this.IDBKeyRange = IDBKeyRange;
this.IDBDatabase = IDBDatabase;
this.IDBObjectStore = IDBObjectStore;
this.resetIDB = resetIDB;
this.MessageEvent = MessageEvent;
this.MessageChannel = MessageChannel;
this.MessagePort = MessagePort;
this.Notification = Notification;

@@ -74,20 +113,21 @@ this.NotificationEvent = NotificationEvent;

this.PushSubscription = PushSubscription;
this.performance = new Performance();
this.Request = Request;
this.Response = Response;
this.SyncEvent = SyncEvent;
this.ServiceWorkerGlobalScope = ServiceWorkerGlobalScope;
this.URL = URL;
this.MessageEvent = MessageEvent;
this.URLSearchParams = URLSearchParams;
this.navigator = {};
this.navigator.userAgent = options.userAgent;
// Instance variable to avoid issues with `this`
this.addEventListener = (name, callback) => {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
this.listeners[name].push(callback);
};
this.WindowClient = WindowClient;
// Instance variable to avoid issues with `this`
this.trigger = (name, args) => {
if (this.listeners[name]) {
return eventHandler(name, args, this.listeners[name]);
if (this.listeners.has(name)) {
return eventHandler(
name,
args,
Array.from(this.listeners.get(name).values())
);
}

@@ -94,0 +134,0 @@ return Promise.resolve();

@@ -0,9 +1,11 @@

// Blob
// https://w3c.github.io/FileAPI/#dom-blob-blob
class Blob {
constructor(parts, options) {
if (parts && !Array.isArray(parts)) {
constructor(parts = [], options = {}) {
if (!Array.isArray(parts)) {
throw new TypeError('Blob requires an array');
}
this.parts = parts || [];
this.type = (options && options.type) || '';
this.parts = parts;
this.type = options.type || '';
}

@@ -13,13 +15,26 @@

return this.parts.reduce((size, part) => {
return size + (part.length || part.size);
return size + (part instanceof Blob ? part.size : String(part).length);
}, 0);
}
get text() {
// Warning: non-standard, but used in other mocks for simplicity.
get _text() {
return this.parts.reduce((text, part) => {
return text + (typeof part === 'string' ? part : part.text);
return text + (part instanceof Blob ? part._text : String(part));
}, '');
}
clone() {
return new Blob(this.parts.slice(), {
type: this.type
});
}
slice(start, end, type) {
const bodyString = this._text;
const slicedBodyString = bodyString.substring(start, end);
return new Blob([slicedBodyString], { type });
}
}
module.exports = Blob;

@@ -10,3 +10,3 @@ const Blob = require('./Blob');

this.bodyUsed = false;
this.body = body === null || body instanceof Blob ? body : new Blob([body]);
this.body = body === null || body instanceof Blob ? body : new Blob([].concat(body));
}

@@ -18,11 +18,11 @@ arrayBuffer() {

blob() {
return this.resolve('blob', body => new Blob([body]));
return this.resolve('blob', body => body);
}
json() {
return this.resolve('json', body => JSON.parse(body.text));
return this.resolve('json', body => JSON.parse(body._text));
}
text() {
return this.resolve('text', body => body.text);
return this.resolve('text', body => body._text);
}

@@ -29,0 +29,0 @@

@@ -41,3 +41,3 @@ // https://developer.mozilla.org/en-US/docs/Web/API/Cache

request = new Request(request);
// Add relative url as well
// Add relative url as well (non-standard)
this.store.set(relativeUrl, { request, response });

@@ -55,4 +55,29 @@ }

keys() {
// https://w3c.github.io/ServiceWorker/#dom-cache-keys
keys(request, options = {}) {
let req = null;
if (request instanceof Request) {
req = request;
if (request.method !== 'GET' && !options.ignoreMethod) {
return Promise.resolve([]);
}
} else if (typeof request === 'string') {
try {
req = new Request(request);
} catch (err) {
return Promise.reject(err);
}
}
const values = Array.from(this.store.values());
if (req) {
return Promise.resolve(values
.filter((value) => {
return value.request.url === req.url;
})
.map((value) => value.request)
);
}
return Promise.resolve(values.map((value) => value.request));

@@ -59,0 +84,0 @@ }

@@ -9,4 +9,10 @@ const Cache = require('./Cache');

match(request) {
async match(request, options = {}) {
const url = request.url || request;
if (options.cacheName) {
const cache = await this.open(options.cacheName);
return cache.match(request);
}
const keys = Object.keys(this.caches);

@@ -19,3 +25,3 @@ for (let i = 0; i < keys.length; i += 1) {

}
return Promise.resolve(null);
return null;
}

@@ -22,0 +28,0 @@

const generateRandomId = require('../utils/generateRandomId');
const EventTarget = require('./EventTarget');
const MessageEvent = require('./MessageEvent');
const MessagePort = require('./MessagePort');
// https://developer.mozilla.org/en-US/docs/Web/API/Client
class Client {
class Client extends EventTarget {
constructor(url, type, frameType) {
super();
this.id = generateRandomId();

@@ -12,4 +17,10 @@ this.url = url;

postMessage() {
throw new Error('METHOD NOT IMPLEMENTED');
// TODO: Implement Transferable
postMessage(message, transfer = []) {
const ports = transfer.filter(objOrPort => (objOrPort instanceof MessagePort));
const event = new MessageEvent('message', {
data: message,
ports
});
this.dispatchEvent(event);
}

@@ -16,0 +27,0 @@

@@ -0,13 +1,47 @@

// Derived from https://github.com/GoogleChrome/workbox
const Event = require('./Event');
// ExtendableEvent
// https://www.w3.org/TR/service-workers-1/#extendable-event
class ExtendableEvent {
constructor() {
this.promise = null;
class ExtendableEvent extends Event {
constructor(...args) {
super(...args);
this.response = null;
// https://www.w3.org/TR/service-workers-1/#dfn-extend-lifetime-promises
this._extendLifetimePromises = new Set();
// Used to keep track of all ExtendableEvent instances.
_allExtendableEvents.add(this);
}
waitUntil(promise) {
this.promise = promise;
this._extendLifetimePromises.add(promise);
}
}
// WORKBOX TODO: if workbox wants to use service-worker-mocks only,
// it needs to migrate at https://github.com/GoogleChrome/workbox/blob/912080a1bf3255c61151ca3d0ebd0895aaf377e2/test/workbox-google-analytics/node/test-index.mjs#L19
// and import `eventsDoneWaiting` from the `ExtendableEvent`
let _allExtendableEvents = new Set();
ExtendableEvent._allExtendableEvents = _allExtendableEvents;
ExtendableEvent.eventsDoneWaiting = () => {
const allExtendLifetimePromises = [];
// Create a single list of _extendLifetimePromises values in all events.
// Also add `catch` handlers to each promise so all of them are run, rather
// that the normal behavior `Promise.all` erroring at the first error.
for (const event of _allExtendableEvents) {
const extendLifetimePromisesOrErrors = [...event._extendLifetimePromises]
.map((promise) => promise.catch((err) => err));
allExtendLifetimePromises.push(...extendLifetimePromisesOrErrors);
}
return Promise.all(allExtendLifetimePromises);
};
module.exports = ExtendableEvent;

@@ -0,9 +1,17 @@

// Derived from https://github.com/GoogleChrome/workbox
const ExtendableEvent = require('./ExtendableEvent');
const Request = require('./Request');
// FetchEvent
// https://www.w3.org/TR/service-workers-1/#fetch-event-section
class FetchEvent extends ExtendableEvent {
constructor(type, init) {
super();
this.type = type;
this.isReload = init.isReload || false;
constructor(type, init = {}) {
super(type, init);
if (!init.request) {
throw new TypeError('Failed to construct \'FetchEvent\': ' +
'Missing required member(s): request.');
}
if (init.request instanceof Request) {

@@ -14,5 +22,15 @@ this.request = init.request;

}
this.clientId = init.clientId || null;
this.isReload = init.isReload || false;
this._respondWithEnteredFlag = false;
}
respondWith(response) {
this.promise = response;
respondWith(promise) {
if (this._respondWithEnteredFlag) {
throw new DOMException('Failed to execute \'respondWith\' on ' +
'\'FetchEvent\': The event has already been responded to.');
}
this._respondWithEnteredFlag = true;
this._extendLifetimePromises.add(promise);
}

@@ -19,0 +37,0 @@ }

@@ -5,6 +5,15 @@ // stubs https://developer.mozilla.org/en-US/docs/Web/API/Headers

constructor(meta) {
if (typeof meta === 'undefined') {
// https://github.com/GoogleChrome/workbox/issues/1461
console.warn('Constructing headers with an undefined argument fails in '
+ 'Chrome <= 56 and Samsung Internet ~6.4. You should use `new Headers({})`.'
);
}
if (meta && meta instanceof Headers) {
this._map = new Map(meta._map);
} else if (meta && typeof meta === 'object') {
this._map = new Map(Object.entries(meta));
this._map = new Map(Object.entries(meta)
.map(entry => [entry[0].toLowerCase(), entry[1]])
);
} else {

@@ -11,0 +20,0 @@ this._map = new Map();

@@ -13,3 +13,3 @@ const ExtendableEvent = require('./ExtendableEvent');

constructor(type, init) {
super();
super(type);
Object.assign(this, defaults(), init);

@@ -16,0 +16,0 @@ }

// stubs https://developer.mozilla.org/en-US/docs/Web/API/Request
const Body = require('./Body');
const Headers = require('./Headers');
const URL = require('dom-urls');
const URL = require('url').URL || require('dom-urls');

@@ -16,10 +16,40 @@

class Request extends Body {
constructor(url, options) {
super(options ? options.body : undefined, options);
this.url = ((url instanceof URL) ? url : new URL(url, self.location.href)).href;
this.method = (options && options.method) || 'GET';
this.mode = (options && options.mode) || 'same-origin'; // FF defaults to cors
constructor(urlOrRequest, options = {}) {
let url = urlOrRequest;
if (urlOrRequest instanceof Request) {
url = urlOrRequest.url;
options = Object.assign({}, {
body: urlOrRequest.body,
credentials: urlOrRequest.credentials,
headers: urlOrRequest.headers,
method: urlOrRequest.method,
mode: urlOrRequest.mode
}, options);
} else if (typeof url === 'string' && url.length === 0) {
url = '/';
}
if (!url) {
throw new TypeError(`Invalid url: ${urlOrRequest}`);
}
super(options.body, options);
if (url instanceof URL) {
this.url = url.href;
} else if (self.useRawRequestUrl) {
this.url = url;
} else {
this.url = new URL(url, self.location.href).href;
}
this.method = options.method || 'GET';
this.mode = options.mode || 'same-origin'; // FF defaults to cors
// See https://fetch.spec.whatwg.org/#concept-request-credentials-mode
this.credentials = options.credentials || (this.mode === 'navigate'
? 'include'
: 'omit');
// Transform options.headers to Headers object
if (options && options.headers) {
if (options.headers) {
if (options.headers instanceof Headers) {

@@ -38,7 +68,11 @@ this.headers = options.headers;

clone() {
if (this.bodyUsed) throwBodyUsed();
if (this.bodyUsed) {
throwBodyUsed();
}
return new Request(this.url, {
method: this.method,
mode: this.mode,
headers: this.headers
headers: this.headers,
body: this.body ? this.body.clone() : this.body
});

@@ -45,0 +79,0 @@ }

// stubs https://developer.mozilla.org/en-US/docs/Web/API/Response
const Body = require('./Body');
const Headers = require('./Headers');

@@ -10,15 +11,29 @@ const isSupportedBodyType = (body) =>

class Response extends Body {
constructor(body = null, init) {
constructor(body = null, options = {}) {
if (!isSupportedBodyType(body)) {
throw new TypeError('Response body must be one of: Blob, USVString, null');
}
super(body);
this.status = (init && typeof init.status === 'number') ? init.status : 200;
super(body, options);
this.status = typeof options.status === 'number'
? options.status
: 200;
this.ok = this.status >= 200 && this.status < 300;
this.statusText = (init && init.statusText) || 'OK';
this.headers = (init && init.headers);
this.statusText = options.statusText || 'OK';
if (options.headers) {
if (options.headers instanceof Headers) {
this.headers = options.headers;
} else if (typeof options.headers === 'object') {
this.headers = new Headers(options.headers);
} else {
throw new TypeError('Cannot construct response.headers: invalid data');
}
} else {
this.headers = new Headers({});
}
this.type = this.status === 0 ? 'opaque' : 'basic';
this.redirected = false;
this.url = (init && init.url) || 'http://example.com/asset';
this.url = options.url || 'http://example.com/asset';
this.method = options.method || 'GET';
}

@@ -25,0 +40,0 @@

@@ -5,6 +5,10 @@ const PushManager = require('./PushManager');

const NotificationEvent = require('./NotificationEvent');
const SyncManager = require('./SyncManager');
const EventTarget = require('./EventTarget');
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
class ServiceWorkerRegistration {
class ServiceWorkerRegistration extends EventTarget {
constructor() {
super();
this.active = null;

@@ -15,2 +19,3 @@ this.installing = null;

this.navigationPreload = new NavigationPreloadManager();
this.sync = new SyncManager();
this.scope = '/';

@@ -17,0 +22,0 @@ this.waiting = null;

{
"name": "service-worker-mock",
"version": "1.12.0",
"version": "2.0.0",
"main": "index.js",

@@ -26,4 +26,7 @@ "repository": {

"dependencies": {
"dom-urls": "^1.1.0"
"dom-urls": "^1.1.0",
"shelving-mock-indexeddb": "github:philipwalton/shelving-mock-indexeddb#621b7a275568051846d20e3e557fa1101418b1d1",
"url-search-params": "^0.10.0",
"w3c-hr-time": "^1.0.1"
}
}

@@ -26,3 +26,3 @@ const ExtendableEvent = require('../models/ExtendableEvent');

callback(event);
return Promise.resolve(event.promise);
return Promise.all(Array.from(event._extendLifetimePromises.values()));
}

@@ -29,0 +29,0 @@

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc