feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
380
web/node_modules/dompurify/src/attrs.ts
generated
vendored
Normal file
380
web/node_modules/dompurify/src/attrs.ts
generated
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
import { freeze } from './utils.js';
|
||||
|
||||
export const html = freeze([
|
||||
'accept',
|
||||
'action',
|
||||
'align',
|
||||
'alt',
|
||||
'autocapitalize',
|
||||
'autocomplete',
|
||||
'autopictureinpicture',
|
||||
'autoplay',
|
||||
'background',
|
||||
'bgcolor',
|
||||
'border',
|
||||
'capture',
|
||||
'cellpadding',
|
||||
'cellspacing',
|
||||
'checked',
|
||||
'cite',
|
||||
'class',
|
||||
'clear',
|
||||
'color',
|
||||
'cols',
|
||||
'colspan',
|
||||
'command',
|
||||
'commandfor',
|
||||
'controls',
|
||||
'controlslist',
|
||||
'coords',
|
||||
'crossorigin',
|
||||
'datetime',
|
||||
'decoding',
|
||||
'default',
|
||||
'dir',
|
||||
'disabled',
|
||||
'disablepictureinpicture',
|
||||
'disableremoteplayback',
|
||||
'download',
|
||||
'draggable',
|
||||
'enctype',
|
||||
'enterkeyhint',
|
||||
'exportparts',
|
||||
'face',
|
||||
'for',
|
||||
'headers',
|
||||
'height',
|
||||
'hidden',
|
||||
'high',
|
||||
'href',
|
||||
'hreflang',
|
||||
'id',
|
||||
'inert',
|
||||
'inputmode',
|
||||
'integrity',
|
||||
'ismap',
|
||||
'kind',
|
||||
'label',
|
||||
'lang',
|
||||
'list',
|
||||
'loading',
|
||||
'loop',
|
||||
'low',
|
||||
'max',
|
||||
'maxlength',
|
||||
'media',
|
||||
'method',
|
||||
'min',
|
||||
'minlength',
|
||||
'multiple',
|
||||
'muted',
|
||||
'name',
|
||||
'nonce',
|
||||
'noshade',
|
||||
'novalidate',
|
||||
'nowrap',
|
||||
'open',
|
||||
'optimum',
|
||||
'part',
|
||||
'pattern',
|
||||
'placeholder',
|
||||
'playsinline',
|
||||
'popover',
|
||||
'popovertarget',
|
||||
'popovertargetaction',
|
||||
'poster',
|
||||
'preload',
|
||||
'pubdate',
|
||||
'radiogroup',
|
||||
'readonly',
|
||||
'rel',
|
||||
'required',
|
||||
'rev',
|
||||
'reversed',
|
||||
'role',
|
||||
'rows',
|
||||
'rowspan',
|
||||
'spellcheck',
|
||||
'scope',
|
||||
'selected',
|
||||
'shape',
|
||||
'size',
|
||||
'sizes',
|
||||
'slot',
|
||||
'span',
|
||||
'srclang',
|
||||
'start',
|
||||
'src',
|
||||
'srcset',
|
||||
'step',
|
||||
'style',
|
||||
'summary',
|
||||
'tabindex',
|
||||
'title',
|
||||
'translate',
|
||||
'type',
|
||||
'usemap',
|
||||
'valign',
|
||||
'value',
|
||||
'width',
|
||||
'wrap',
|
||||
'xmlns',
|
||||
] as const);
|
||||
|
||||
export const svg = freeze([
|
||||
'accent-height',
|
||||
'accumulate',
|
||||
'additive',
|
||||
'alignment-baseline',
|
||||
'amplitude',
|
||||
'ascent',
|
||||
'attributename',
|
||||
'attributetype',
|
||||
'azimuth',
|
||||
'basefrequency',
|
||||
'baseline-shift',
|
||||
'begin',
|
||||
'bias',
|
||||
'by',
|
||||
'class',
|
||||
'clip',
|
||||
'clippathunits',
|
||||
'clip-path',
|
||||
'clip-rule',
|
||||
'color',
|
||||
'color-interpolation',
|
||||
'color-interpolation-filters',
|
||||
'color-profile',
|
||||
'color-rendering',
|
||||
'cx',
|
||||
'cy',
|
||||
'd',
|
||||
'dx',
|
||||
'dy',
|
||||
'diffuseconstant',
|
||||
'direction',
|
||||
'display',
|
||||
'divisor',
|
||||
'dominant-baseline',
|
||||
'dur',
|
||||
'edgemode',
|
||||
'elevation',
|
||||
'end',
|
||||
'exponent',
|
||||
'fill',
|
||||
'fill-opacity',
|
||||
'fill-rule',
|
||||
'filter',
|
||||
'filterunits',
|
||||
'flood-color',
|
||||
'flood-opacity',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-weight',
|
||||
'fx',
|
||||
'fy',
|
||||
'g1',
|
||||
'g2',
|
||||
'glyph-name',
|
||||
'glyphref',
|
||||
'gradientunits',
|
||||
'gradienttransform',
|
||||
'height',
|
||||
'href',
|
||||
'id',
|
||||
'image-rendering',
|
||||
'in',
|
||||
'in2',
|
||||
'intercept',
|
||||
'k',
|
||||
'k1',
|
||||
'k2',
|
||||
'k3',
|
||||
'k4',
|
||||
'kerning',
|
||||
'keypoints',
|
||||
'keysplines',
|
||||
'keytimes',
|
||||
'lang',
|
||||
'lengthadjust',
|
||||
'letter-spacing',
|
||||
'kernelmatrix',
|
||||
'kernelunitlength',
|
||||
'lighting-color',
|
||||
'local',
|
||||
'marker-end',
|
||||
'marker-mid',
|
||||
'marker-start',
|
||||
'markerheight',
|
||||
'markerunits',
|
||||
'markerwidth',
|
||||
'maskcontentunits',
|
||||
'maskunits',
|
||||
'max',
|
||||
'mask',
|
||||
'mask-type',
|
||||
'media',
|
||||
'method',
|
||||
'mode',
|
||||
'min',
|
||||
'name',
|
||||
'numoctaves',
|
||||
'offset',
|
||||
'operator',
|
||||
'opacity',
|
||||
'order',
|
||||
'orient',
|
||||
'orientation',
|
||||
'origin',
|
||||
'overflow',
|
||||
'paint-order',
|
||||
'path',
|
||||
'pathlength',
|
||||
'patterncontentunits',
|
||||
'patterntransform',
|
||||
'patternunits',
|
||||
'points',
|
||||
'preservealpha',
|
||||
'preserveaspectratio',
|
||||
'primitiveunits',
|
||||
'r',
|
||||
'rx',
|
||||
'ry',
|
||||
'radius',
|
||||
'refx',
|
||||
'refy',
|
||||
'repeatcount',
|
||||
'repeatdur',
|
||||
'restart',
|
||||
'result',
|
||||
'rotate',
|
||||
'scale',
|
||||
'seed',
|
||||
'shape-rendering',
|
||||
'slope',
|
||||
'specularconstant',
|
||||
'specularexponent',
|
||||
'spreadmethod',
|
||||
'startoffset',
|
||||
'stddeviation',
|
||||
'stitchtiles',
|
||||
'stop-color',
|
||||
'stop-opacity',
|
||||
'stroke-dasharray',
|
||||
'stroke-dashoffset',
|
||||
'stroke-linecap',
|
||||
'stroke-linejoin',
|
||||
'stroke-miterlimit',
|
||||
'stroke-opacity',
|
||||
'stroke',
|
||||
'stroke-width',
|
||||
'style',
|
||||
'surfacescale',
|
||||
'systemlanguage',
|
||||
'tabindex',
|
||||
'tablevalues',
|
||||
'targetx',
|
||||
'targety',
|
||||
'transform',
|
||||
'transform-origin',
|
||||
'text-anchor',
|
||||
'text-decoration',
|
||||
'text-orientation',
|
||||
'text-rendering',
|
||||
'textlength',
|
||||
'type',
|
||||
'u1',
|
||||
'u2',
|
||||
'unicode',
|
||||
'values',
|
||||
'viewbox',
|
||||
'visibility',
|
||||
'version',
|
||||
'vert-adv-y',
|
||||
'vert-origin-x',
|
||||
'vert-origin-y',
|
||||
'width',
|
||||
'word-spacing',
|
||||
'wrap',
|
||||
'writing-mode',
|
||||
'xchannelselector',
|
||||
'ychannelselector',
|
||||
'x',
|
||||
'x1',
|
||||
'x2',
|
||||
'xmlns',
|
||||
'y',
|
||||
'y1',
|
||||
'y2',
|
||||
'z',
|
||||
'zoomandpan',
|
||||
] as const);
|
||||
|
||||
export const mathMl = freeze([
|
||||
'accent',
|
||||
'accentunder',
|
||||
'align',
|
||||
'bevelled',
|
||||
'close',
|
||||
'columnalign',
|
||||
'columnlines',
|
||||
'columnspacing',
|
||||
'columnspan',
|
||||
'denomalign',
|
||||
'depth',
|
||||
'dir',
|
||||
'display',
|
||||
'displaystyle',
|
||||
'encoding',
|
||||
'fence',
|
||||
'frame',
|
||||
'height',
|
||||
'href',
|
||||
'id',
|
||||
'largeop',
|
||||
'length',
|
||||
'linethickness',
|
||||
'lquote',
|
||||
'lspace',
|
||||
'mathbackground',
|
||||
'mathcolor',
|
||||
'mathsize',
|
||||
'mathvariant',
|
||||
'maxsize',
|
||||
'minsize',
|
||||
'movablelimits',
|
||||
'notation',
|
||||
'numalign',
|
||||
'open',
|
||||
'rowalign',
|
||||
'rowlines',
|
||||
'rowspacing',
|
||||
'rowspan',
|
||||
'rspace',
|
||||
'rquote',
|
||||
'scriptlevel',
|
||||
'scriptminsize',
|
||||
'scriptsizemultiplier',
|
||||
'selection',
|
||||
'separator',
|
||||
'separators',
|
||||
'stretchy',
|
||||
'subscriptshift',
|
||||
'supscriptshift',
|
||||
'symmetric',
|
||||
'voffset',
|
||||
'width',
|
||||
'xmlns',
|
||||
]);
|
||||
|
||||
export const xml = freeze([
|
||||
'xlink:href',
|
||||
'xml:id',
|
||||
'xlink:title',
|
||||
'xml:space',
|
||||
'xmlns:xlink',
|
||||
] as const);
|
||||
257
web/node_modules/dompurify/src/config.ts
generated
vendored
Normal file
257
web/node_modules/dompurify/src/config.ts
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
import type { TrustedTypePolicy } from 'trusted-types/lib/index.js';
|
||||
|
||||
/**
|
||||
* Configuration to control DOMPurify behavior.
|
||||
*/
|
||||
export interface Config {
|
||||
/**
|
||||
* Extend the existing array of allowed attributes.
|
||||
* Can be an array of attribute names, or a function that receives
|
||||
* the attribute name and tag name to determine if the attribute is allowed.
|
||||
*/
|
||||
ADD_ATTR?:
|
||||
| string[]
|
||||
| ((attributeName: string, tagName: string) => boolean)
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of elements that can use Data URIs.
|
||||
*/
|
||||
ADD_DATA_URI_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of allowed tags.
|
||||
* Can be an array of tag names, or a function that receives
|
||||
* the tag name to determine if the tag is allowed.
|
||||
*/
|
||||
ADD_TAGS?: string[] | ((tagName: string) => boolean) | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing array of elements that are safe for URI-like values (be careful, XSS risk).
|
||||
*/
|
||||
ADD_URI_SAFE_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow ARIA attributes, leave other safe HTML as is (default is true).
|
||||
*/
|
||||
ALLOW_ARIA_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow HTML5 data attributes, leave other safe HTML as is (default is true).
|
||||
*/
|
||||
ALLOW_DATA_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow external protocol handlers in URL attributes (default is false, be careful, XSS risk).
|
||||
* By default only `http`, `https`, `ftp`, `ftps`, `tel`, `mailto`, `callto`, `sms`, `cid` and `xmpp` are allowed.
|
||||
*/
|
||||
ALLOW_UNKNOWN_PROTOCOLS?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Decide if self-closing tags in attributes are allowed.
|
||||
* Usually removed due to a mXSS issue in jQuery 3.0.
|
||||
*/
|
||||
ALLOW_SELF_CLOSE_IN_ATTR?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific attributes.
|
||||
*/
|
||||
ALLOWED_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific elements.
|
||||
*/
|
||||
ALLOWED_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow only specific namespaces. Defaults to:
|
||||
* - `http://www.w3.org/1999/xhtml`
|
||||
* - `http://www.w3.org/2000/svg`
|
||||
* - `http://www.w3.org/1998/Math/MathML`
|
||||
*/
|
||||
ALLOWED_NAMESPACES?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Allow specific protocols handlers in URL attributes via regex (be careful, XSS risk).
|
||||
* Default RegExp:
|
||||
* ```
|
||||
* /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
|
||||
* ```
|
||||
*/
|
||||
ALLOWED_URI_REGEXP?: RegExp | undefined;
|
||||
|
||||
/**
|
||||
* Define how custom elements are handled.
|
||||
*/
|
||||
CUSTOM_ELEMENT_HANDLING?: {
|
||||
/**
|
||||
* Regular expression or function to match to allowed elements.
|
||||
* Default is null (disallow any custom elements).
|
||||
*/
|
||||
tagNameCheck?: RegExp | ((tagName: string) => boolean) | null | undefined;
|
||||
|
||||
/**
|
||||
* Regular expression or function to match to allowed attributes.
|
||||
* Default is null (disallow any attributes not on the allow list).
|
||||
*/
|
||||
attributeNameCheck?:
|
||||
| RegExp
|
||||
| ((attributeName: string, tagName?: string) => boolean)
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* Allow custom elements derived from built-ins if they pass `tagNameCheck`. Default is false.
|
||||
*/
|
||||
allowCustomizedBuiltInElements?: boolean | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add attributes to block-list.
|
||||
*/
|
||||
FORBID_ATTR?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Add child elements to be removed when their parent is removed.
|
||||
*/
|
||||
FORBID_CONTENTS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Extend the existing or default array of forbidden content elements.
|
||||
*/
|
||||
ADD_FORBID_CONTENTS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Add elements to block-list.
|
||||
*/
|
||||
FORBID_TAGS?: string[] | undefined;
|
||||
|
||||
/**
|
||||
* Glue elements like style, script or others to `document.body` and prevent unintuitive browser behavior in several edge-cases (default is false).
|
||||
*/
|
||||
FORCE_BODY?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Map of non-standard HTML element names to support. Map to true to enable support. For example:
|
||||
*
|
||||
* ```
|
||||
* HTML_INTEGRATION_POINTS: { foreignobject: true }
|
||||
* ```
|
||||
*/
|
||||
HTML_INTEGRATION_POINTS?: Record<string, boolean> | undefined;
|
||||
|
||||
/**
|
||||
* Sanitize a node "in place", which is much faster depending on how you use DOMPurify.
|
||||
*/
|
||||
IN_PLACE?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Keep an element's content when the element is removed (default is true).
|
||||
*/
|
||||
KEEP_CONTENT?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Map of MathML element names to support. Map to true to enable support. For example:
|
||||
*
|
||||
* ```
|
||||
* MATHML_TEXT_INTEGRATION_POINTS: { mtext: true }
|
||||
* ```
|
||||
*/
|
||||
MATHML_TEXT_INTEGRATION_POINTS?: Record<string, boolean> | undefined;
|
||||
|
||||
/**
|
||||
* Change the default namespace from HTML to something different.
|
||||
*/
|
||||
NAMESPACE?: string | undefined;
|
||||
|
||||
/**
|
||||
* Change the parser type so sanitized data is treated as XML and not as HTML, which is the default.
|
||||
*/
|
||||
PARSER_MEDIA_TYPE?: DOMParserSupportedType | undefined;
|
||||
|
||||
/**
|
||||
* Return a DOM `DocumentFragment` instead of an HTML string (default is false).
|
||||
*/
|
||||
RETURN_DOM_FRAGMENT?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Return a DOM `HTMLBodyElement` instead of an HTML string (default is false).
|
||||
*/
|
||||
RETURN_DOM?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Return a TrustedHTML object instead of a string if possible.
|
||||
*/
|
||||
RETURN_TRUSTED_TYPE?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Strip `{{ ... }}`, `${ ... }` and `<% ... %>` to make output safe for template systems.
|
||||
* Be careful please, this mode is not recommended for production usage.
|
||||
* Allowing template parsing in user-controlled HTML is not advised at all.
|
||||
* Only use this mode if there is really no alternative.
|
||||
*/
|
||||
SAFE_FOR_TEMPLATES?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Change how e.g. comments containing risky HTML characters are treated.
|
||||
* Be very careful, this setting should only be set to `false` if you really only handle
|
||||
* HTML and nothing else, no SVG, MathML or the like.
|
||||
* Otherwise, changing from `true` to `false` will lead to XSS in this or some other way.
|
||||
*/
|
||||
SAFE_FOR_XML?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Use DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here).
|
||||
*/
|
||||
SANITIZE_DOM?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Enforce strict DOM Clobbering protection via namespace isolation (default is false).
|
||||
* When enabled, isolates the namespace of named properties (i.e., `id` and `name` attributes)
|
||||
* from JS variables by prefixing them with the string `user-content-`
|
||||
*/
|
||||
SANITIZE_NAMED_PROPS?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Supplied policy must define `createHTML` and `createScriptURL`.
|
||||
*/
|
||||
TRUSTED_TYPES_POLICY?: TrustedTypePolicy | null | undefined;
|
||||
|
||||
/**
|
||||
* Controls categories of allowed elements.
|
||||
*
|
||||
* Note that the `USE_PROFILES` setting will override the `ALLOWED_TAGS` setting
|
||||
* so don't use them together.
|
||||
*/
|
||||
USE_PROFILES?: false | UseProfilesConfig | undefined;
|
||||
|
||||
/**
|
||||
* Return entire document including <html> tags (default is false).
|
||||
*/
|
||||
WHOLE_DOCUMENT?: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines categories of allowed elements.
|
||||
*/
|
||||
export interface UseProfilesConfig {
|
||||
/**
|
||||
* Allow all safe MathML elements.
|
||||
*/
|
||||
mathMl?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe SVG elements.
|
||||
*/
|
||||
svg?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe SVG Filters.
|
||||
*/
|
||||
svgFilters?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Allow all safe HTML elements.
|
||||
*/
|
||||
html?: boolean | undefined;
|
||||
}
|
||||
1
web/node_modules/dompurify/src/license_header
generated
vendored
Normal file
1
web/node_modules/dompurify/src/license_header
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/*! @license DOMPurify VERSION | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/VERSION/LICENSE */
|
||||
2990
web/node_modules/dompurify/src/purify.ts
generated
vendored
Normal file
2990
web/node_modules/dompurify/src/purify.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
web/node_modules/dompurify/src/regexp.ts
generated
vendored
Normal file
24
web/node_modules/dompurify/src/regexp.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { seal } from './utils.js';
|
||||
|
||||
export const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
|
||||
export const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
|
||||
export const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
|
||||
export const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
||||
export const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
||||
export const IS_ALLOWED_URI = seal(
|
||||
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
||||
);
|
||||
export const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
||||
export const ATTR_WHITESPACE = seal(
|
||||
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
||||
);
|
||||
export const DOCTYPE_NAME = seal(/^html$/i);
|
||||
export const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
||||
|
||||
// Markup-significant character probes used by _sanitizeElements.
|
||||
// Shared module-level instances are safe despite the sticky /g flags:
|
||||
// unapply() resets lastIndex for RegExp receivers before every call.
|
||||
export const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
|
||||
export const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
|
||||
export const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
|
||||
export const SELF_CLOSING_TAG = seal(/\/>/i);
|
||||
285
web/node_modules/dompurify/src/tags.ts
generated
vendored
Normal file
285
web/node_modules/dompurify/src/tags.ts
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
import { freeze } from './utils.js';
|
||||
|
||||
export const html = freeze([
|
||||
'a',
|
||||
'abbr',
|
||||
'acronym',
|
||||
'address',
|
||||
'area',
|
||||
'article',
|
||||
'aside',
|
||||
'audio',
|
||||
'b',
|
||||
'bdi',
|
||||
'bdo',
|
||||
'big',
|
||||
'blink',
|
||||
'blockquote',
|
||||
'body',
|
||||
'br',
|
||||
'button',
|
||||
'canvas',
|
||||
'caption',
|
||||
'center',
|
||||
'cite',
|
||||
'code',
|
||||
'col',
|
||||
'colgroup',
|
||||
'content',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'decorator',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dialog',
|
||||
'dir',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'element',
|
||||
'em',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'font',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'marquee',
|
||||
'menu',
|
||||
'menuitem',
|
||||
'meter',
|
||||
'nav',
|
||||
'nobr',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'picture',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rp',
|
||||
'rt',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'search',
|
||||
'section',
|
||||
'select',
|
||||
'shadow',
|
||||
'slot',
|
||||
'small',
|
||||
'source',
|
||||
'spacer',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'style',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'tr',
|
||||
'track',
|
||||
'tt',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr',
|
||||
] as const);
|
||||
|
||||
export const svg = freeze([
|
||||
'svg',
|
||||
'a',
|
||||
'altglyph',
|
||||
'altglyphdef',
|
||||
'altglyphitem',
|
||||
'animatecolor',
|
||||
'animatemotion',
|
||||
'animatetransform',
|
||||
'circle',
|
||||
'clippath',
|
||||
'defs',
|
||||
'desc',
|
||||
'ellipse',
|
||||
'enterkeyhint',
|
||||
'exportparts',
|
||||
'filter',
|
||||
'font',
|
||||
'g',
|
||||
'glyph',
|
||||
'glyphref',
|
||||
'hkern',
|
||||
'image',
|
||||
'inputmode',
|
||||
'line',
|
||||
'lineargradient',
|
||||
'marker',
|
||||
'mask',
|
||||
'metadata',
|
||||
'mpath',
|
||||
'part',
|
||||
'path',
|
||||
'pattern',
|
||||
'polygon',
|
||||
'polyline',
|
||||
'radialgradient',
|
||||
'rect',
|
||||
'stop',
|
||||
'style',
|
||||
'switch',
|
||||
'symbol',
|
||||
'text',
|
||||
'textpath',
|
||||
'title',
|
||||
'tref',
|
||||
'tspan',
|
||||
'view',
|
||||
'vkern',
|
||||
] as const);
|
||||
|
||||
export const svgFilters = freeze([
|
||||
'feBlend',
|
||||
'feColorMatrix',
|
||||
'feComponentTransfer',
|
||||
'feComposite',
|
||||
'feConvolveMatrix',
|
||||
'feDiffuseLighting',
|
||||
'feDisplacementMap',
|
||||
'feDistantLight',
|
||||
'feDropShadow',
|
||||
'feFlood',
|
||||
'feFuncA',
|
||||
'feFuncB',
|
||||
'feFuncG',
|
||||
'feFuncR',
|
||||
'feGaussianBlur',
|
||||
'feImage',
|
||||
'feMerge',
|
||||
'feMergeNode',
|
||||
'feMorphology',
|
||||
'feOffset',
|
||||
'fePointLight',
|
||||
'feSpecularLighting',
|
||||
'feSpotLight',
|
||||
'feTile',
|
||||
'feTurbulence',
|
||||
] as const);
|
||||
|
||||
// List of SVG elements that are disallowed by default.
|
||||
// We still need to know them so that we can do namespace
|
||||
// checks properly in case one wants to add them to
|
||||
// allow-list.
|
||||
export const svgDisallowed = freeze([
|
||||
'animate',
|
||||
'color-profile',
|
||||
'cursor',
|
||||
'discard',
|
||||
'font-face',
|
||||
'font-face-format',
|
||||
'font-face-name',
|
||||
'font-face-src',
|
||||
'font-face-uri',
|
||||
'foreignobject',
|
||||
'hatch',
|
||||
'hatchpath',
|
||||
'mesh',
|
||||
'meshgradient',
|
||||
'meshpatch',
|
||||
'meshrow',
|
||||
'missing-glyph',
|
||||
'script',
|
||||
'set',
|
||||
'solidcolor',
|
||||
'unknown',
|
||||
'use',
|
||||
] as const);
|
||||
|
||||
export const mathMl = freeze([
|
||||
'math',
|
||||
'menclose',
|
||||
'merror',
|
||||
'mfenced',
|
||||
'mfrac',
|
||||
'mglyph',
|
||||
'mi',
|
||||
'mlabeledtr',
|
||||
'mmultiscripts',
|
||||
'mn',
|
||||
'mo',
|
||||
'mover',
|
||||
'mpadded',
|
||||
'mphantom',
|
||||
'mroot',
|
||||
'mrow',
|
||||
'ms',
|
||||
'mspace',
|
||||
'msqrt',
|
||||
'mstyle',
|
||||
'msub',
|
||||
'msup',
|
||||
'msubsup',
|
||||
'mtable',
|
||||
'mtd',
|
||||
'mtext',
|
||||
'mtr',
|
||||
'munder',
|
||||
'munderover',
|
||||
'mprescripts',
|
||||
] as const);
|
||||
|
||||
// Similarly to SVG, we want to know all MathML elements,
|
||||
// even those that we disallow by default.
|
||||
export const mathMlDisallowed = freeze([
|
||||
'maction',
|
||||
'maligngroup',
|
||||
'malignmark',
|
||||
'mlongdiv',
|
||||
'mscarries',
|
||||
'mscarry',
|
||||
'msgroup',
|
||||
'mstack',
|
||||
'msline',
|
||||
'msrow',
|
||||
'semantics',
|
||||
'annotation',
|
||||
'annotation-xml',
|
||||
'mprescripts',
|
||||
'none',
|
||||
] as const);
|
||||
|
||||
export const text = freeze(['#text'] as const);
|
||||
354
web/node_modules/dompurify/src/types.ts
generated
vendored
Normal file
354
web/node_modules/dompurify/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
import type {
|
||||
TrustedHTML,
|
||||
TrustedTypesWindow,
|
||||
} from 'trusted-types/lib/index.js';
|
||||
import type { Config } from './config';
|
||||
|
||||
export interface DOMPurify {
|
||||
/**
|
||||
* Creates a DOMPurify instance using the given window-like object. Defaults to `window`.
|
||||
*/
|
||||
(root?: WindowLike): DOMPurify;
|
||||
|
||||
/**
|
||||
* Version label, exposed for easier checks
|
||||
* if DOMPurify is up to date or not
|
||||
*/
|
||||
version: string;
|
||||
|
||||
/**
|
||||
* Array of elements that DOMPurify removed during sanitation.
|
||||
* Empty if nothing was removed.
|
||||
*/
|
||||
removed: Array<RemovedElement | RemovedAttribute>;
|
||||
|
||||
/**
|
||||
* Expose whether this browser supports running the full DOMPurify.
|
||||
*/
|
||||
isSupported: boolean;
|
||||
|
||||
/**
|
||||
* Set the configuration once.
|
||||
*
|
||||
* @param cfg configuration object
|
||||
*/
|
||||
setConfig(cfg?: Config): void;
|
||||
|
||||
/**
|
||||
* Removes the configuration.
|
||||
*/
|
||||
clearConfig(): void;
|
||||
|
||||
/**
|
||||
* Provides core sanitation functionality.
|
||||
*
|
||||
* @param dirty string or DOM node
|
||||
* @param cfg object
|
||||
* @returns Sanitized TrustedHTML.
|
||||
*/
|
||||
sanitize(
|
||||
dirty: string | Node,
|
||||
cfg: Config & { RETURN_TRUSTED_TYPE: true }
|
||||
): TrustedHTML;
|
||||
|
||||
/**
|
||||
* Provides core sanitation functionality.
|
||||
*
|
||||
* @param dirty DOM node
|
||||
* @param cfg object
|
||||
* @returns Sanitized DOM node.
|
||||
*/
|
||||
sanitize(dirty: Node, cfg: Config & { IN_PLACE: true }): Node;
|
||||
|
||||
/**
|
||||
* Provides core sanitation functionality.
|
||||
*
|
||||
* @param dirty string or DOM node
|
||||
* @param cfg object
|
||||
* @returns Sanitized DOM node.
|
||||
*/
|
||||
sanitize(dirty: string | Node, cfg: Config & { RETURN_DOM: true }): Node;
|
||||
|
||||
/**
|
||||
* Provides core sanitation functionality.
|
||||
*
|
||||
* @param dirty string or DOM node
|
||||
* @param cfg object
|
||||
* @returns Sanitized document fragment.
|
||||
*/
|
||||
sanitize(
|
||||
dirty: string | Node,
|
||||
cfg: Config & { RETURN_DOM_FRAGMENT: true }
|
||||
): DocumentFragment;
|
||||
|
||||
/**
|
||||
* Provides core sanitation functionality.
|
||||
*
|
||||
* @param dirty string or DOM node
|
||||
* @param cfg object
|
||||
* @returns Sanitized string.
|
||||
*/
|
||||
sanitize(dirty: string | Node, cfg?: Config): string;
|
||||
|
||||
/**
|
||||
* Checks if an attribute value is valid.
|
||||
* Uses last set config, if any. Otherwise, uses config defaults.
|
||||
*
|
||||
* @param tag Tag name of containing element.
|
||||
* @param attr Attribute name.
|
||||
* @param value Attribute value.
|
||||
* @returns Returns true if `value` is valid. Otherwise, returns false.
|
||||
*/
|
||||
isValidAttribute(tag: string, attr: string, value: string): boolean;
|
||||
|
||||
/**
|
||||
* Adds a DOMPurify hook.
|
||||
*
|
||||
* @param entryPoint entry point for the hook to add
|
||||
* @param hookFunction function to execute
|
||||
*/
|
||||
addHook(entryPoint: BasicHookName, hookFunction: NodeHook): void;
|
||||
|
||||
/**
|
||||
* Adds a DOMPurify hook.
|
||||
*
|
||||
* @param entryPoint entry point for the hook to add
|
||||
* @param hookFunction function to execute
|
||||
*/
|
||||
addHook(entryPoint: ElementHookName, hookFunction: ElementHook): void;
|
||||
|
||||
/**
|
||||
* Adds a DOMPurify hook.
|
||||
*
|
||||
* @param entryPoint entry point for the hook to add
|
||||
* @param hookFunction function to execute
|
||||
*/
|
||||
addHook(
|
||||
entryPoint: DocumentFragmentHookName,
|
||||
hookFunction: DocumentFragmentHook
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Adds a DOMPurify hook.
|
||||
*
|
||||
* @param entryPoint entry point for the hook to add
|
||||
* @param hookFunction function to execute
|
||||
*/
|
||||
addHook(
|
||||
entryPoint: 'uponSanitizeElement',
|
||||
hookFunction: UponSanitizeElementHook
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Adds a DOMPurify hook.
|
||||
*
|
||||
* @param entryPoint entry point for the hook to add
|
||||
* @param hookFunction function to execute
|
||||
*/
|
||||
addHook(
|
||||
entryPoint: 'uponSanitizeAttribute',
|
||||
hookFunction: UponSanitizeAttributeHook
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Remove a DOMPurify hook at a given entryPoint
|
||||
* (pops it from the stack of hooks if hook not specified)
|
||||
*
|
||||
* @param entryPoint entry point for the hook to remove
|
||||
* @param hookFunction optional specific hook to remove
|
||||
* @returns removed hook
|
||||
*/
|
||||
removeHook(
|
||||
entryPoint: BasicHookName,
|
||||
hookFunction?: NodeHook
|
||||
): NodeHook | undefined;
|
||||
|
||||
/**
|
||||
* Remove a DOMPurify hook at a given entryPoint
|
||||
* (pops it from the stack of hooks if hook not specified)
|
||||
*
|
||||
* @param entryPoint entry point for the hook to remove
|
||||
* @param hookFunction optional specific hook to remove
|
||||
* @returns removed hook
|
||||
*/
|
||||
removeHook(
|
||||
entryPoint: ElementHookName,
|
||||
hookFunction?: ElementHook
|
||||
): ElementHook | undefined;
|
||||
|
||||
/**
|
||||
* Remove a DOMPurify hook at a given entryPoint
|
||||
* (pops it from the stack of hooks if hook not specified)
|
||||
*
|
||||
* @param entryPoint entry point for the hook to remove
|
||||
* @param hookFunction optional specific hook to remove
|
||||
* @returns removed hook
|
||||
*/
|
||||
removeHook(
|
||||
entryPoint: DocumentFragmentHookName,
|
||||
hookFunction?: DocumentFragmentHook
|
||||
): DocumentFragmentHook | undefined;
|
||||
|
||||
/**
|
||||
* Remove a DOMPurify hook at a given entryPoint
|
||||
* (pops it from the stack of hooks if hook not specified)
|
||||
*
|
||||
* @param entryPoint entry point for the hook to remove
|
||||
* @param hookFunction optional specific hook to remove
|
||||
* @returns removed hook
|
||||
*/
|
||||
removeHook(
|
||||
entryPoint: 'uponSanitizeElement',
|
||||
hookFunction?: UponSanitizeElementHook
|
||||
): UponSanitizeElementHook | undefined;
|
||||
|
||||
/**
|
||||
* Remove a DOMPurify hook at a given entryPoint
|
||||
* (pops it from the stack of hooks if hook not specified)
|
||||
*
|
||||
* @param entryPoint entry point for the hook to remove
|
||||
* @param hookFunction optional specific hook to remove
|
||||
* @returns removed hook
|
||||
*/
|
||||
removeHook(
|
||||
entryPoint: 'uponSanitizeAttribute',
|
||||
hookFunction?: UponSanitizeAttributeHook
|
||||
): UponSanitizeAttributeHook | undefined;
|
||||
|
||||
/**
|
||||
* Removes all DOMPurify hooks at a given entryPoint
|
||||
*
|
||||
* @param entryPoint entry point for the hooks to remove
|
||||
*/
|
||||
removeHooks(entryPoint: HookName): void;
|
||||
|
||||
/**
|
||||
* Removes all DOMPurify hooks.
|
||||
*/
|
||||
removeAllHooks(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An element removed by DOMPurify.
|
||||
*/
|
||||
export interface RemovedElement {
|
||||
/**
|
||||
* The element that was removed.
|
||||
*/
|
||||
element: Node;
|
||||
}
|
||||
|
||||
/**
|
||||
* An element removed by DOMPurify.
|
||||
*/
|
||||
export interface RemovedAttribute {
|
||||
/**
|
||||
* The attribute that was removed.
|
||||
*/
|
||||
attribute: Attr | null;
|
||||
|
||||
/**
|
||||
* The element that the attribute was removed.
|
||||
*/
|
||||
from: Node;
|
||||
}
|
||||
|
||||
type BasicHookName =
|
||||
| 'beforeSanitizeElements'
|
||||
| 'afterSanitizeElements'
|
||||
| 'uponSanitizeShadowNode';
|
||||
type ElementHookName = 'beforeSanitizeAttributes' | 'afterSanitizeAttributes';
|
||||
type DocumentFragmentHookName =
|
||||
| 'beforeSanitizeShadowDOM'
|
||||
| 'afterSanitizeShadowDOM';
|
||||
type UponSanitizeElementHookName = 'uponSanitizeElement';
|
||||
type UponSanitizeAttributeHookName = 'uponSanitizeAttribute';
|
||||
|
||||
export interface HooksMap {
|
||||
beforeSanitizeElements: NodeHook[];
|
||||
afterSanitizeElements: NodeHook[];
|
||||
beforeSanitizeShadowDOM: DocumentFragmentHook[];
|
||||
uponSanitizeShadowNode: NodeHook[];
|
||||
afterSanitizeShadowDOM: DocumentFragmentHook[];
|
||||
beforeSanitizeAttributes: ElementHook[];
|
||||
afterSanitizeAttributes: ElementHook[];
|
||||
uponSanitizeElement: UponSanitizeElementHook[];
|
||||
uponSanitizeAttribute: UponSanitizeAttributeHook[];
|
||||
}
|
||||
|
||||
type ArrayElement<T> = T extends Array<infer U> ? U : never;
|
||||
|
||||
export type HookFunction = ArrayElement<HooksMap[keyof HooksMap]>;
|
||||
|
||||
export type HookName =
|
||||
| BasicHookName
|
||||
| ElementHookName
|
||||
| DocumentFragmentHookName
|
||||
| UponSanitizeElementHookName
|
||||
| UponSanitizeAttributeHookName;
|
||||
|
||||
export type NodeHook = (
|
||||
this: DOMPurify,
|
||||
currentNode: Node,
|
||||
hookEvent: null,
|
||||
config: Config
|
||||
) => void;
|
||||
|
||||
export type ElementHook = (
|
||||
this: DOMPurify,
|
||||
currentNode: Element,
|
||||
hookEvent: null,
|
||||
config: Config
|
||||
) => void;
|
||||
|
||||
export type DocumentFragmentHook = (
|
||||
this: DOMPurify,
|
||||
currentNode: DocumentFragment,
|
||||
hookEvent: null,
|
||||
config: Config
|
||||
) => void;
|
||||
|
||||
export type UponSanitizeElementHook = (
|
||||
this: DOMPurify,
|
||||
currentNode: Node,
|
||||
hookEvent: UponSanitizeElementHookEvent,
|
||||
config: Config
|
||||
) => void;
|
||||
|
||||
export type UponSanitizeAttributeHook = (
|
||||
this: DOMPurify,
|
||||
currentNode: Element,
|
||||
hookEvent: UponSanitizeAttributeHookEvent,
|
||||
config: Config
|
||||
) => void;
|
||||
|
||||
export interface UponSanitizeElementHookEvent {
|
||||
tagName: string;
|
||||
allowedTags: Record<string, boolean>;
|
||||
}
|
||||
|
||||
export interface UponSanitizeAttributeHookEvent {
|
||||
attrName: string;
|
||||
attrValue: string;
|
||||
keepAttr: boolean;
|
||||
allowedAttributes: Record<string, boolean>;
|
||||
forceKeepAttr: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Window`-like object containing the properties and types that DOMPurify requires.
|
||||
*/
|
||||
export type WindowLike = Pick<
|
||||
typeof globalThis,
|
||||
| 'DocumentFragment'
|
||||
| 'HTMLTemplateElement'
|
||||
| 'Node'
|
||||
| 'Element'
|
||||
| 'NodeFilter'
|
||||
| 'NamedNodeMap'
|
||||
| 'HTMLFormElement'
|
||||
| 'DOMParser'
|
||||
> & {
|
||||
document?: Document;
|
||||
MozNamedAttrMap?: typeof window.NamedNodeMap;
|
||||
} & Pick<TrustedTypesWindow, 'trustedTypes'>;
|
||||
338
web/node_modules/dompurify/src/utils.ts
generated
vendored
Normal file
338
web/node_modules/dompurify/src/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
const {
|
||||
entries,
|
||||
setPrototypeOf,
|
||||
isFrozen,
|
||||
getPrototypeOf,
|
||||
getOwnPropertyDescriptor,
|
||||
} = Object;
|
||||
|
||||
let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
|
||||
let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
|
||||
|
||||
if (!freeze) {
|
||||
freeze = function <T>(x: T): T {
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
if (!seal) {
|
||||
seal = function <T>(x: T): T {
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
if (!apply) {
|
||||
apply = function <T>(
|
||||
func: (thisArg: any, ...args: any[]) => T,
|
||||
thisArg: any,
|
||||
...args: any[]
|
||||
): T {
|
||||
return func.apply(thisArg, args);
|
||||
};
|
||||
}
|
||||
|
||||
if (!construct) {
|
||||
construct = function <T>(Func: new (...args: any[]) => T, ...args: any[]): T {
|
||||
return new Func(...args);
|
||||
};
|
||||
}
|
||||
|
||||
const arrayForEach = unapply(Array.prototype.forEach);
|
||||
const arrayIndexOf = unapply(Array.prototype.indexOf);
|
||||
const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
||||
const arrayPop = unapply(Array.prototype.pop);
|
||||
const arrayPush = unapply(Array.prototype.push);
|
||||
const arraySlice = unapply(Array.prototype.slice);
|
||||
const arraySplice = unapply(Array.prototype.splice);
|
||||
const arrayIsArray = Array.isArray;
|
||||
|
||||
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
||||
const stringToString = unapply(String.prototype.toString);
|
||||
const stringMatch = unapply(String.prototype.match);
|
||||
const stringReplace = unapply(String.prototype.replace);
|
||||
const stringIndexOf = unapply(String.prototype.indexOf);
|
||||
const stringTrim = unapply(String.prototype.trim);
|
||||
|
||||
const numberToString = unapply(Number.prototype.toString);
|
||||
const booleanToString = unapply(Boolean.prototype.toString);
|
||||
const bigintToString =
|
||||
typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
|
||||
const symbolToString =
|
||||
typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
|
||||
|
||||
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
||||
const objectToString = unapply(Object.prototype.toString);
|
||||
|
||||
const regExpTest = unapply(RegExp.prototype.test);
|
||||
|
||||
const typeErrorCreate = unconstruct(TypeError);
|
||||
|
||||
/**
|
||||
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
||||
*
|
||||
* @param func - The function to be wrapped and called.
|
||||
* @returns A new function that calls the given function with a specified thisArg and arguments.
|
||||
*/
|
||||
function unapply<T>(
|
||||
func: (thisArg: any, ...args: any[]) => T
|
||||
): (thisArg: any, ...args: any[]) => T {
|
||||
return (thisArg: any, ...args: any[]): T => {
|
||||
if (thisArg instanceof RegExp) {
|
||||
thisArg.lastIndex = 0;
|
||||
}
|
||||
|
||||
return apply(func, thisArg, args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
||||
*
|
||||
* @param func - The constructor function to be wrapped and called.
|
||||
* @returns A new function that constructs an instance of the given constructor function with the provided arguments.
|
||||
*/
|
||||
function unconstruct<T>(
|
||||
Func: new (...args: any[]) => T
|
||||
): (...args: any[]) => T {
|
||||
return (...args: any[]): T => construct(Func, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties to a lookup table
|
||||
*
|
||||
* @param set - The set to which elements will be added.
|
||||
* @param array - The array containing elements to be added to the set.
|
||||
* @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
||||
* @returns The modified set with added elements.
|
||||
*/
|
||||
function addToSet(
|
||||
set: Record<string, boolean>,
|
||||
array: readonly unknown[],
|
||||
transformCaseFunc: ReturnType<typeof unapply<string>> = stringToLowerCase
|
||||
): Record<string, boolean> {
|
||||
if (setPrototypeOf) {
|
||||
// Make 'in' and truthy checks like Boolean(set.constructor)
|
||||
// independent of any properties defined on Object.prototype.
|
||||
// Prevent prototype setters from intercepting set as a this value.
|
||||
setPrototypeOf(set, null);
|
||||
}
|
||||
|
||||
if (!arrayIsArray(array)) {
|
||||
return set;
|
||||
}
|
||||
|
||||
let l = array.length;
|
||||
while (l--) {
|
||||
let element = array[l];
|
||||
|
||||
if (typeof element === 'string') {
|
||||
const lcElement = transformCaseFunc(element);
|
||||
|
||||
if (lcElement !== element) {
|
||||
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
||||
if (!isFrozen(array)) {
|
||||
(array as unknown[])[l] = lcElement;
|
||||
}
|
||||
|
||||
element = lcElement;
|
||||
}
|
||||
}
|
||||
|
||||
set[element as string] = true;
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up an array to harden against CSPP
|
||||
*
|
||||
* @param array - The array to be cleaned.
|
||||
* @returns The cleaned version of the array
|
||||
*/
|
||||
function cleanArray<T>(array: T[]): Array<T | null> {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const isPropertyExist = objectHasOwnProperty(array, index);
|
||||
|
||||
if (!isPropertyExist) {
|
||||
array[index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shallow clone an object
|
||||
*
|
||||
* @param object - The object to be cloned.
|
||||
* @returns A new object that copies the original.
|
||||
*/
|
||||
function clone<T extends Record<string, any>>(object: T): T {
|
||||
const newObject = create(null);
|
||||
|
||||
for (const [property, value] of entries(object)) {
|
||||
const isPropertyExist = objectHasOwnProperty(object, property);
|
||||
|
||||
if (isPropertyExist) {
|
||||
if (arrayIsArray(value)) {
|
||||
newObject[property] = cleanArray(value);
|
||||
} else if (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
value.constructor === Object
|
||||
) {
|
||||
newObject[property] = clone(value);
|
||||
} else {
|
||||
newObject[property] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert non-node values into strings without depending on direct property access.
|
||||
*
|
||||
* @param value - The value to stringify.
|
||||
* @returns A string representation of the provided value.
|
||||
*/
|
||||
function stringifyValue(value: unknown): string {
|
||||
switch (typeof value) {
|
||||
case 'string': {
|
||||
return value;
|
||||
}
|
||||
|
||||
case 'number': {
|
||||
return numberToString(value);
|
||||
}
|
||||
|
||||
case 'boolean': {
|
||||
return booleanToString(value);
|
||||
}
|
||||
|
||||
case 'bigint': {
|
||||
return bigintToString ? bigintToString(value) : '0';
|
||||
}
|
||||
|
||||
case 'symbol': {
|
||||
return symbolToString ? symbolToString(value) : 'Symbol()';
|
||||
}
|
||||
|
||||
case 'undefined': {
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
case 'function':
|
||||
case 'object': {
|
||||
if (value === null) {
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
const valueAsRecord = value as Record<string, any>;
|
||||
const valueToString = lookupGetter(valueAsRecord, 'toString');
|
||||
|
||||
if (typeof valueToString === 'function') {
|
||||
const stringified = valueToString(valueAsRecord);
|
||||
|
||||
return typeof stringified === 'string'
|
||||
? stringified
|
||||
: objectToString(stringified);
|
||||
}
|
||||
|
||||
return objectToString(value);
|
||||
}
|
||||
|
||||
default: {
|
||||
return objectToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
||||
*
|
||||
* @param object - The object to look up the getter function in its prototype chain.
|
||||
* @param prop - The property name for which to find the getter function.
|
||||
* @returns The getter function found in the prototype chain or a fallback function.
|
||||
*/
|
||||
function lookupGetter<T extends Record<string, any>>(
|
||||
object: T,
|
||||
prop: string
|
||||
): ReturnType<typeof unapply<any>> | (() => null) {
|
||||
while (object !== null) {
|
||||
const desc = getOwnPropertyDescriptor(object, prop);
|
||||
|
||||
if (desc) {
|
||||
if (desc.get) {
|
||||
return unapply(desc.get);
|
||||
}
|
||||
|
||||
if (typeof desc.value === 'function') {
|
||||
return unapply(desc.value);
|
||||
}
|
||||
}
|
||||
|
||||
object = getPrototypeOf(object);
|
||||
}
|
||||
|
||||
function fallbackValue(): null {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
function isRegex(value: unknown): value is RegExp {
|
||||
try {
|
||||
regExpTest(value as RegExp, '');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
// Array
|
||||
arrayForEach,
|
||||
arrayIndexOf,
|
||||
arrayIsArray,
|
||||
arrayLastIndexOf,
|
||||
arrayPop,
|
||||
arrayPush,
|
||||
arraySlice,
|
||||
arraySplice,
|
||||
// Object
|
||||
entries,
|
||||
freeze,
|
||||
getPrototypeOf,
|
||||
getOwnPropertyDescriptor,
|
||||
isFrozen,
|
||||
setPrototypeOf,
|
||||
seal,
|
||||
clone,
|
||||
create,
|
||||
objectHasOwnProperty,
|
||||
objectToString,
|
||||
// RegExp
|
||||
regExpTest,
|
||||
isRegex,
|
||||
// String
|
||||
stringIndexOf,
|
||||
stringMatch,
|
||||
stringReplace,
|
||||
stringToLowerCase,
|
||||
stringToString,
|
||||
stringTrim,
|
||||
// Other conversion
|
||||
stringifyValue,
|
||||
// Errors
|
||||
typeErrorCreate,
|
||||
// Other
|
||||
lookupGetter,
|
||||
addToSet,
|
||||
// Reflect
|
||||
unapply,
|
||||
unconstruct,
|
||||
};
|
||||
Reference in New Issue
Block a user