Initial commit: Developer Tools MVP with visual editor

- Complete React app with 7 developer tools
- JSON Tool with visual structured editor
- Serialize Tool with visual structured editor
- URL, Base64, CSV/JSON, Beautifier, Diff tools
- Responsive navigation with dropdown menu
- Dark/light mode toggle
- Mobile-responsive design with sticky header
- All tools working with copy/paste functionality
This commit is contained in:
dwindown
2025-08-02 09:31:26 +07:00
commit 7f2dd5260f
45657 changed files with 4730486 additions and 0 deletions

19
node_modules/workbox-core/_private/Deferred.d.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import '../_version.js';
/**
* The Deferred class composes Promises in a way that allows for them to be
* resolved or rejected from outside the constructor. In most cases promises
* should be used directly, but Deferreds can be necessary when the logic to
* resolve a promise must be separate.
*
* @private
*/
declare class Deferred<T> {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (reason?: any) => void;
/**
* Creates a promise and exposes its resolve and reject functions as methods.
*/
constructor();
}
export { Deferred };

28
node_modules/workbox-core/_private/Deferred.js generated vendored Normal file
View File

@@ -0,0 +1,28 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
/**
* The Deferred class composes Promises in a way that allows for them to be
* resolved or rejected from outside the constructor. In most cases promises
* should be used directly, but Deferreds can be necessary when the logic to
* resolve a promise must be separate.
*
* @private
*/
class Deferred {
/**
* Creates a promise and exposes its resolve and reject functions as methods.
*/
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
export { Deferred };

1
node_modules/workbox-core/_private/Deferred.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './Deferred.js';

24
node_modules/workbox-core/_private/WorkboxError.d.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import { MapLikeObject } from '../types.js';
import '../_version.js';
/**
* Workbox errors should be thrown with this class.
* This allows use to ensure the type easily in tests,
* helps developers identify errors from workbox
* easily and allows use to optimise error
* messages correctly.
*
* @private
*/
declare class WorkboxError extends Error {
details?: MapLikeObject;
/**
*
* @param {string} errorCode The error code that
* identifies this particular error.
* @param {Object=} details Any relevant arguments
* that will help developers identify issues should
* be added as a key on the context object.
*/
constructor(errorCode: string, details?: MapLikeObject);
}
export { WorkboxError };

35
node_modules/workbox-core/_private/WorkboxError.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import { messageGenerator } from '../models/messages/messageGenerator.js';
import '../_version.js';
/**
* Workbox errors should be thrown with this class.
* This allows use to ensure the type easily in tests,
* helps developers identify errors from workbox
* easily and allows use to optimise error
* messages correctly.
*
* @private
*/
class WorkboxError extends Error {
/**
*
* @param {string} errorCode The error code that
* identifies this particular error.
* @param {Object=} details Any relevant arguments
* that will help developers identify issues should
* be added as a key on the context object.
*/
constructor(errorCode, details) {
const message = messageGenerator(errorCode, details);
super(message);
this.name = errorCode;
this.details = details;
}
}
export { WorkboxError };

1
node_modules/workbox-core/_private/WorkboxError.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './WorkboxError.js';

11
node_modules/workbox-core/_private/assert.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import { MapLikeObject } from '../types.js';
import '../_version.js';
declare const finalAssertExports: {
hasMethod: (object: MapLikeObject, expectedMethod: string, details: MapLikeObject) => void;
isArray: (value: any[], details: MapLikeObject) => void;
isInstance: (object: unknown, expectedClass: Function, details: MapLikeObject) => void;
isOneOf: (value: any, validValues: any[], details: MapLikeObject) => void;
isType: (object: unknown, expectedType: string, details: MapLikeObject) => void;
isArrayOfClass: (value: any, expectedClass: Function, details: MapLikeObject) => void;
} | null;
export { finalAssertExports as assert };

73
node_modules/workbox-core/_private/assert.js generated vendored Normal file
View File

@@ -0,0 +1,73 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import { WorkboxError } from '../_private/WorkboxError.js';
import '../_version.js';
/*
* This method throws if the supplied value is not an array.
* The destructed values are required to produce a meaningful error for users.
* The destructed and restructured object is so it's clear what is
* needed.
*/
const isArray = (value, details) => {
if (!Array.isArray(value)) {
throw new WorkboxError('not-an-array', details);
}
};
const hasMethod = (object, expectedMethod, details) => {
const type = typeof object[expectedMethod];
if (type !== 'function') {
details['expectedMethod'] = expectedMethod;
throw new WorkboxError('missing-a-method', details);
}
};
const isType = (object, expectedType, details) => {
if (typeof object !== expectedType) {
details['expectedType'] = expectedType;
throw new WorkboxError('incorrect-type', details);
}
};
const isInstance = (object,
// Need the general type to do the check later.
// eslint-disable-next-line @typescript-eslint/ban-types
expectedClass, details) => {
if (!(object instanceof expectedClass)) {
details['expectedClassName'] = expectedClass.name;
throw new WorkboxError('incorrect-class', details);
}
};
const isOneOf = (value, validValues, details) => {
if (!validValues.includes(value)) {
details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;
throw new WorkboxError('invalid-value', details);
}
};
const isArrayOfClass = (value,
// Need general type to do check later.
expectedClass, // eslint-disable-line
details) => {
const error = new WorkboxError('not-array-of-class', details);
if (!Array.isArray(value)) {
throw error;
}
for (const item of value) {
if (!(item instanceof expectedClass)) {
throw error;
}
}
};
const finalAssertExports = process.env.NODE_ENV === 'production'
? null
: {
hasMethod,
isArray,
isInstance,
isOneOf,
isType,
isArrayOfClass,
};
export { finalAssertExports as assert };

1
node_modules/workbox-core/_private/assert.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './assert.js';

View File

@@ -0,0 +1,15 @@
import '../_version.js';
/**
* Matches an item in the cache, ignoring specific URL params. This is similar
* to the `ignoreSearch` option, but it allows you to ignore just specific
* params (while continuing to match on the others).
*
* @private
* @param {Cache} cache
* @param {Request} request
* @param {Object} matchOptions
* @param {Array<string>} ignoreParams
* @return {Promise<Response|undefined>}
*/
declare function cacheMatchIgnoreParams(cache: Cache, request: Request, ignoreParams: string[], matchOptions?: CacheQueryOptions): Promise<Response | undefined>;
export { cacheMatchIgnoreParams };

View File

@@ -0,0 +1,44 @@
/*
Copyright 2020 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
function stripParams(fullURL, ignoreParams) {
const strippedURL = new URL(fullURL);
for (const param of ignoreParams) {
strippedURL.searchParams.delete(param);
}
return strippedURL.href;
}
/**
* Matches an item in the cache, ignoring specific URL params. This is similar
* to the `ignoreSearch` option, but it allows you to ignore just specific
* params (while continuing to match on the others).
*
* @private
* @param {Cache} cache
* @param {Request} request
* @param {Object} matchOptions
* @param {Array<string>} ignoreParams
* @return {Promise<Response|undefined>}
*/
async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {
const strippedRequestURL = stripParams(request.url, ignoreParams);
// If the request doesn't include any ignored params, match as normal.
if (request.url === strippedRequestURL) {
return cache.match(request, matchOptions);
}
// Otherwise, match by comparing keys
const keysOptions = Object.assign(Object.assign({}, matchOptions), { ignoreSearch: true });
const cacheKeys = await cache.keys(request, keysOptions);
for (const cacheKey of cacheKeys) {
const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);
if (strippedRequestURL === strippedCacheKeyURL) {
return cache.match(cacheKey, matchOptions);
}
}
return;
}
export { cacheMatchIgnoreParams };

View File

@@ -0,0 +1 @@
export * from './cacheMatchIgnoreParams.js';

20
node_modules/workbox-core/_private/cacheNames.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
import '../_version.js';
export interface CacheNameDetails {
googleAnalytics: string;
precache: string;
prefix: string;
runtime: string;
suffix: string;
}
export interface PartialCacheNameDetails {
[propName: string]: string;
}
export type CacheNameDetailsProp = 'googleAnalytics' | 'precache' | 'prefix' | 'runtime' | 'suffix';
export declare const cacheNames: {
updateDetails: (details: PartialCacheNameDetails) => void;
getGoogleAnalyticsName: (userCacheName?: string) => string;
getPrecacheName: (userCacheName?: string) => string;
getPrefix: () => string;
getRuntimeName: (userCacheName?: string) => string;
getSuffix: () => string;
};

49
node_modules/workbox-core/_private/cacheNames.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
const _cacheNameDetails = {
googleAnalytics: 'googleAnalytics',
precache: 'precache-v2',
prefix: 'workbox',
runtime: 'runtime',
suffix: typeof registration !== 'undefined' ? registration.scope : '',
};
const _createCacheName = (cacheName) => {
return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix]
.filter((value) => value && value.length > 0)
.join('-');
};
const eachCacheNameDetail = (fn) => {
for (const key of Object.keys(_cacheNameDetails)) {
fn(key);
}
};
export const cacheNames = {
updateDetails: (details) => {
eachCacheNameDetail((key) => {
if (typeof details[key] === 'string') {
_cacheNameDetails[key] = details[key];
}
});
},
getGoogleAnalyticsName: (userCacheName) => {
return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);
},
getPrecacheName: (userCacheName) => {
return userCacheName || _createCacheName(_cacheNameDetails.precache);
},
getPrefix: () => {
return _cacheNameDetails.prefix;
},
getRuntimeName: (userCacheName) => {
return userCacheName || _createCacheName(_cacheNameDetails.runtime);
},
getSuffix: () => {
return _cacheNameDetails.suffix;
},
};

1
node_modules/workbox-core/_private/cacheNames.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './cacheNames.js';

View File

@@ -0,0 +1,13 @@
import '../_version.js';
/**
* A utility function that determines whether the current browser supports
* constructing a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
* object.
*
* @return {boolean} `true`, if the current browser can successfully
* construct a `ReadableStream`, `false` otherwise.
*
* @private
*/
declare function canConstructReadableStream(): boolean;
export { canConstructReadableStream };

View File

@@ -0,0 +1,33 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
let supportStatus;
/**
* A utility function that determines whether the current browser supports
* constructing a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
* object.
*
* @return {boolean} `true`, if the current browser can successfully
* construct a `ReadableStream`, `false` otherwise.
*
* @private
*/
function canConstructReadableStream() {
if (supportStatus === undefined) {
// See https://github.com/GoogleChrome/workbox/issues/1473
try {
new ReadableStream({ start() { } });
supportStatus = true;
}
catch (error) {
supportStatus = false;
}
}
return supportStatus;
}
export { canConstructReadableStream };

View File

@@ -0,0 +1 @@
export * from './canConstructReadableStream.js';

View File

@@ -0,0 +1,12 @@
import '../_version.js';
/**
* A utility function that determines whether the current browser supports
* constructing a new `Response` from a `response.body` stream.
*
* @return {boolean} `true`, if the current browser can successfully
* construct a `Response` from a `response.body` stream, `false` otherwise.
*
* @private
*/
declare function canConstructResponseFromBodyStream(): boolean;
export { canConstructResponseFromBodyStream };

View File

@@ -0,0 +1,35 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
let supportStatus;
/**
* A utility function that determines whether the current browser supports
* constructing a new `Response` from a `response.body` stream.
*
* @return {boolean} `true`, if the current browser can successfully
* construct a `Response` from a `response.body` stream, `false` otherwise.
*
* @private
*/
function canConstructResponseFromBodyStream() {
if (supportStatus === undefined) {
const testResponse = new Response('');
if ('body' in testResponse) {
try {
new Response(testResponse.body);
supportStatus = true;
}
catch (error) {
supportStatus = false;
}
}
supportStatus = false;
}
return supportStatus;
}
export { canConstructResponseFromBodyStream };

View File

@@ -0,0 +1 @@
export * from './canConstructResponseFromBodyStream.js';

7
node_modules/workbox-core/_private/dontWaitFor.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import '../_version.js';
/**
* A helper function that prevents a promise from being flagged as unused.
*
* @private
**/
export declare function dontWaitFor(promise: Promise<any>): void;

16
node_modules/workbox-core/_private/dontWaitFor.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
/**
* A helper function that prevents a promise from being flagged as unused.
*
* @private
**/
export function dontWaitFor(promise) {
// Effective no-op.
void promise.then(() => { });
}

1
node_modules/workbox-core/_private/dontWaitFor.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './dontWaitFor.js';

View File

@@ -0,0 +1,10 @@
import '../_version.js';
/**
* Runs all of the callback functions, one at a time sequentially, in the order
* in which they were registered.
*
* @memberof workbox-core
* @private
*/
declare function executeQuotaErrorCallbacks(): Promise<void>;
export { executeQuotaErrorCallbacks };

View File

@@ -0,0 +1,33 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import { logger } from '../_private/logger.js';
import { quotaErrorCallbacks } from '../models/quotaErrorCallbacks.js';
import '../_version.js';
/**
* Runs all of the callback functions, one at a time sequentially, in the order
* in which they were registered.
*
* @memberof workbox-core
* @private
*/
async function executeQuotaErrorCallbacks() {
if (process.env.NODE_ENV !== 'production') {
logger.log(`About to run ${quotaErrorCallbacks.size} ` +
`callbacks to clean up caches.`);
}
for (const callback of quotaErrorCallbacks) {
await callback();
if (process.env.NODE_ENV !== 'production') {
logger.log(callback, 'is complete.');
}
}
if (process.env.NODE_ENV !== 'production') {
logger.log('Finished running callbacks.');
}
}
export { executeQuotaErrorCallbacks };

View File

@@ -0,0 +1 @@
export * from './executeQuotaErrorCallbacks.js';

View File

@@ -0,0 +1,3 @@
import '../_version.js';
declare const getFriendlyURL: (url: URL | string) => string;
export { getFriendlyURL };

15
node_modules/workbox-core/_private/getFriendlyURL.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
const getFriendlyURL = (url) => {
const urlObj = new URL(String(url), location.href);
// See https://github.com/GoogleChrome/workbox/issues/2323
// We want to include everything, except for the origin if it's same-origin.
return urlObj.href.replace(new RegExp(`^${location.origin}`), '');
};
export { getFriendlyURL };

View File

@@ -0,0 +1 @@
export * from './getFriendlyURL.js';

11
node_modules/workbox-core/_private/logger.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import '../_version.js';
declare global {
interface WorkerGlobalScope {
__WB_DISABLE_DEV_LOGS: boolean;
}
interface Window {
__WB_DISABLE_DEV_LOGS: boolean;
}
}
declare const logger: Console;
export { logger };

65
node_modules/workbox-core/_private/logger.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
const logger = (process.env.NODE_ENV === 'production'
? null
: (() => {
// Don't overwrite this value if it's already set.
// See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {
self.__WB_DISABLE_DEV_LOGS = false;
}
let inGroup = false;
const methodToColorMap = {
debug: `#7f8c8d`,
log: `#2ecc71`,
warn: `#f39c12`,
error: `#c0392b`,
groupCollapsed: `#3498db`,
groupEnd: null, // No colored prefix on groupEnd
};
const print = function (method, args) {
if (self.__WB_DISABLE_DEV_LOGS) {
return;
}
if (method === 'groupCollapsed') {
// Safari doesn't print all console.groupCollapsed() arguments:
// https://bugs.webkit.org/show_bug.cgi?id=182754
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
console[method](...args);
return;
}
}
const styles = [
`background: ${methodToColorMap[method]}`,
`border-radius: 0.5em`,
`color: white`,
`font-weight: bold`,
`padding: 2px 0.5em`,
];
// When in a group, the workbox prefix is not displayed.
const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
console[method](...logPrefix, ...args);
if (method === 'groupCollapsed') {
inGroup = true;
}
if (method === 'groupEnd') {
inGroup = false;
}
};
// eslint-disable-next-line @typescript-eslint/ban-types
const api = {};
const loggerMethods = Object.keys(methodToColorMap);
for (const key of loggerMethods) {
const method = key;
api[method] = (...args) => {
print(method, args);
};
}
return api;
})());
export { logger };

1
node_modules/workbox-core/_private/logger.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './logger.js';

View File

@@ -0,0 +1,12 @@
import '../_version.js';
/**
* Returns a promise that resolves to a window client matching the passed
* `resultingClientId`. For browsers that don't support `resultingClientId`
* or if waiting for the resulting client to apper takes too long, resolve to
* `undefined`.
*
* @param {string} [resultingClientId]
* @return {Promise<Client|undefined>}
* @private
*/
export declare function resultingClientExists(resultingClientId?: string): Promise<Client | undefined>;

View File

@@ -0,0 +1,48 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import { timeout } from './timeout.js';
import '../_version.js';
const MAX_RETRY_TIME = 2000;
/**
* Returns a promise that resolves to a window client matching the passed
* `resultingClientId`. For browsers that don't support `resultingClientId`
* or if waiting for the resulting client to apper takes too long, resolve to
* `undefined`.
*
* @param {string} [resultingClientId]
* @return {Promise<Client|undefined>}
* @private
*/
export async function resultingClientExists(resultingClientId) {
if (!resultingClientId) {
return;
}
let existingWindows = await self.clients.matchAll({ type: 'window' });
const existingWindowIds = new Set(existingWindows.map((w) => w.id));
let resultingWindow;
const startTime = performance.now();
// Only wait up to `MAX_RETRY_TIME` to find a matching client.
while (performance.now() - startTime < MAX_RETRY_TIME) {
existingWindows = await self.clients.matchAll({ type: 'window' });
resultingWindow = existingWindows.find((w) => {
if (resultingClientId) {
// If we have a `resultingClientId`, we can match on that.
return w.id === resultingClientId;
}
else {
// Otherwise match on finding a window not in `existingWindowIds`.
return !existingWindowIds.has(w.id);
}
});
if (resultingWindow) {
break;
}
// Sleep for 100ms and retry.
await timeout(100);
}
return resultingWindow;
}

View File

@@ -0,0 +1 @@
export * from './resultingClientExists.js';

10
node_modules/workbox-core/_private/timeout.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import '../_version.js';
/**
* Returns a promise that resolves and the passed number of milliseconds.
* This utility is an async/await-friendly version of `setTimeout`.
*
* @param {number} ms
* @return {Promise}
* @private
*/
export declare function timeout(ms: number): Promise<unknown>;

18
node_modules/workbox-core/_private/timeout.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
/**
* Returns a promise that resolves and the passed number of milliseconds.
* This utility is an async/await-friendly version of `setTimeout`.
*
* @param {number} ms
* @return {Promise}
* @private
*/
export function timeout(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

1
node_modules/workbox-core/_private/timeout.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './timeout.js';

12
node_modules/workbox-core/_private/waitUntil.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
import '../_version.js';
/**
* A utility method that makes it easier to use `event.waitUntil` with
* async functions and return the result.
*
* @param {ExtendableEvent} event
* @param {Function} asyncFn
* @return {Function}
* @private
*/
declare function waitUntil(event: ExtendableEvent, asyncFn: () => Promise<any>): Promise<any>;
export { waitUntil };

22
node_modules/workbox-core/_private/waitUntil.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
Copyright 2020 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import '../_version.js';
/**
* A utility method that makes it easier to use `event.waitUntil` with
* async functions and return the result.
*
* @param {ExtendableEvent} event
* @param {Function} asyncFn
* @return {Function}
* @private
*/
function waitUntil(event, asyncFn) {
const returnPromise = asyncFn();
event.waitUntil(returnPromise);
return returnPromise;
}
export { waitUntil };

1
node_modules/workbox-core/_private/waitUntil.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from './waitUntil.js';