@memberjunction/ng-shared-generic 5.38.0 → 5.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/rich-text-sanitize.test.d.ts +2 -0
- package/dist/__tests__/rich-text-sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/rich-text-sanitize.test.js +54 -0
- package/dist/__tests__/rich-text-sanitize.test.js.map +1 -0
- package/dist/lib/_tokens.scss +9 -0
- package/dist/lib/rich-text/rich-text-sanitize.d.ts +23 -0
- package/dist/lib/rich-text/rich-text-sanitize.d.ts.map +1 -0
- package/dist/lib/rich-text/rich-text-sanitize.js +28 -0
- package/dist/lib/rich-text/rich-text-sanitize.js.map +1 -0
- package/dist/lib/rich-text/safe-rich-html.pipe.d.ts +24 -0
- package/dist/lib/rich-text/safe-rich-html.pipe.d.ts.map +1 -0
- package/dist/lib/rich-text/safe-rich-html.pipe.js +35 -0
- package/dist/lib/rich-text/safe-rich-html.pipe.js.map +1 -0
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-text-sanitize.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/rich-text-sanitize.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { PurifyRichTextHtml } from '../lib/rich-text/rich-text-sanitize';
|
|
4
|
+
describe('PurifyRichTextHtml', () => {
|
|
5
|
+
describe('strips dangerous vectors', () => {
|
|
6
|
+
it('removes <script> tags', () => {
|
|
7
|
+
const out = PurifyRichTextHtml('<p>ok</p><script>alert(1)</script>');
|
|
8
|
+
expect(out).toContain('<p>ok</p>');
|
|
9
|
+
expect(out.toLowerCase()).not.toContain('<script');
|
|
10
|
+
expect(out).not.toContain('alert(1)');
|
|
11
|
+
});
|
|
12
|
+
it('strips on* event handler attributes', () => {
|
|
13
|
+
const out = PurifyRichTextHtml('<img src="x" onerror="alert(1)">');
|
|
14
|
+
expect(out.toLowerCase()).not.toContain('onerror');
|
|
15
|
+
});
|
|
16
|
+
it('strips javascript: URLs', () => {
|
|
17
|
+
const out = PurifyRichTextHtml('<a href="javascript:alert(1)">x</a>');
|
|
18
|
+
expect(out.toLowerCase()).not.toContain('javascript:');
|
|
19
|
+
});
|
|
20
|
+
it('removes <script> embedded inside SVG', () => {
|
|
21
|
+
const out = PurifyRichTextHtml('<svg><script>alert(1)</script><rect/></svg>');
|
|
22
|
+
expect(out.toLowerCase()).not.toContain('<script');
|
|
23
|
+
expect(out).not.toContain('alert(1)');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('keeps safe rich content', () => {
|
|
27
|
+
it('preserves structural HTML', () => {
|
|
28
|
+
const out = PurifyRichTextHtml('<h2>Title</h2><ul><li>a</li></ul><table><tr><td>c</td></tr></table>');
|
|
29
|
+
expect(out).toContain('<h2>Title</h2>');
|
|
30
|
+
expect(out).toContain('<li>a</li>');
|
|
31
|
+
expect(out).toContain('<td>c</td>');
|
|
32
|
+
});
|
|
33
|
+
it('preserves inline SVG shapes (the whole point of swapping the sanitizer)', () => {
|
|
34
|
+
const svg = '<svg viewBox="0 0 10 10"><defs><linearGradient id="g"><stop offset="0%"/></linearGradient></defs><rect fill="url(#g)"/><path d="M0 0 L10 10"/><circle cx="5" cy="5" r="3"/></svg>';
|
|
35
|
+
const out = PurifyRichTextHtml(svg);
|
|
36
|
+
expect(out.toLowerCase()).toContain('<svg');
|
|
37
|
+
expect(out.toLowerCase()).toContain('<rect');
|
|
38
|
+
expect(out.toLowerCase()).toContain('<path');
|
|
39
|
+
expect(out.toLowerCase()).toContain('<circle');
|
|
40
|
+
expect(out.toLowerCase()).toContain('lineargradient');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('edge cases', () => {
|
|
44
|
+
it('returns empty string for null/undefined/empty', () => {
|
|
45
|
+
expect(PurifyRichTextHtml(null)).toBe('');
|
|
46
|
+
expect(PurifyRichTextHtml(undefined)).toBe('');
|
|
47
|
+
expect(PurifyRichTextHtml('')).toBe('');
|
|
48
|
+
});
|
|
49
|
+
it('passes plain text through unchanged', () => {
|
|
50
|
+
expect(PurifyRichTextHtml('just text')).toBe('just text');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=rich-text-sanitize.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-text-sanitize.test.js","sourceRoot":"","sources":["../../src/__tests__/rich-text-sanitize.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect } from 'vitest';\nimport { PurifyRichTextHtml } from '../lib/rich-text/rich-text-sanitize';\n\ndescribe('PurifyRichTextHtml', () => {\n describe('strips dangerous vectors', () => {\n it('removes <script> tags', () => {\n const out = PurifyRichTextHtml('<p>ok</p><script>alert(1)</script>');\n expect(out).toContain('<p>ok</p>');\n expect(out.toLowerCase()).not.toContain('<script');\n expect(out).not.toContain('alert(1)');\n });\n\n it('strips on* event handler attributes', () => {\n const out = PurifyRichTextHtml('<img src=\"x\" onerror=\"alert(1)\">');\n expect(out.toLowerCase()).not.toContain('onerror');\n });\n\n it('strips javascript: URLs', () => {\n const out = PurifyRichTextHtml('<a href=\"javascript:alert(1)\">x</a>');\n expect(out.toLowerCase()).not.toContain('javascript:');\n });\n\n it('removes <script> embedded inside SVG', () => {\n const out = PurifyRichTextHtml('<svg><script>alert(1)</script><rect/></svg>');\n expect(out.toLowerCase()).not.toContain('<script');\n expect(out).not.toContain('alert(1)');\n });\n });\n\n describe('keeps safe rich content', () => {\n it('preserves structural HTML', () => {\n const out = PurifyRichTextHtml('<h2>Title</h2><ul><li>a</li></ul><table><tr><td>c</td></tr></table>');\n expect(out).toContain('<h2>Title</h2>');\n expect(out).toContain('<li>a</li>');\n expect(out).toContain('<td>c</td>');\n });\n\n it('preserves inline SVG shapes (the whole point of swapping the sanitizer)', () => {\n const svg = '<svg viewBox=\"0 0 10 10\"><defs><linearGradient id=\"g\"><stop offset=\"0%\"/></linearGradient></defs><rect fill=\"url(#g)\"/><path d=\"M0 0 L10 10\"/><circle cx=\"5\" cy=\"5\" r=\"3\"/></svg>';\n const out = PurifyRichTextHtml(svg);\n expect(out.toLowerCase()).toContain('<svg');\n expect(out.toLowerCase()).toContain('<rect');\n expect(out.toLowerCase()).toContain('<path');\n expect(out.toLowerCase()).toContain('<circle');\n expect(out.toLowerCase()).toContain('lineargradient');\n });\n });\n\n describe('edge cases', () => {\n it('returns empty string for null/undefined/empty', () => {\n expect(PurifyRichTextHtml(null)).toBe('');\n expect(PurifyRichTextHtml(undefined)).toBe('');\n expect(PurifyRichTextHtml('')).toBe('');\n });\n\n it('passes plain text through unchanged', () => {\n expect(PurifyRichTextHtml('just text')).toBe('just text');\n });\n });\n});\n"]}
|
package/dist/lib/_tokens.scss
CHANGED
|
@@ -211,6 +211,11 @@
|
|
|
211
211
|
--mj-highlight-subtle: var(--mj-brand-accent-subtle);
|
|
212
212
|
--mj-on-highlight: var(--mj-brand-on-accent);
|
|
213
213
|
|
|
214
|
+
/* Search-match highlight — the <mark> wrapping typed-query matches in lists,
|
|
215
|
+
field labels, FK dropdowns, etc. Muted amber (not the garish browser default). */
|
|
216
|
+
--mj-search-highlight-bg: color-mix(in srgb, var(--mj-status-warning) 24%, transparent);
|
|
217
|
+
--mj-search-highlight-text: var(--mj-text-primary);
|
|
218
|
+
|
|
214
219
|
/* Login Banner Gradient & Waves */
|
|
215
220
|
--mj-login-grad-start: var(--mj-color-brand-900);
|
|
216
221
|
--mj-login-grad-mid: var(--mj-color-brand-700);
|
|
@@ -413,6 +418,10 @@
|
|
|
413
418
|
--mj-status-warning-text: var(--mj-color-warning-100);
|
|
414
419
|
--mj-status-warning-border: var(--mj-color-warning-600);
|
|
415
420
|
|
|
421
|
+
/* Slightly stronger search highlight on dark surfaces so it stays visible. */
|
|
422
|
+
--mj-search-highlight-bg: color-mix(in srgb, var(--mj-status-warning) 30%, transparent);
|
|
423
|
+
--mj-search-highlight-text: var(--mj-text-primary);
|
|
424
|
+
|
|
416
425
|
--mj-status-error-bg: rgba(239, 68, 68, 0.15);
|
|
417
426
|
--mj-status-error-text: var(--mj-color-error-100);
|
|
418
427
|
--mj-status-error-border: var(--mj-color-error-600);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize untrusted HTML for safe rich-text rendering.
|
|
3
|
+
*
|
|
4
|
+
* Use this instead of relying on Angular's built-in `[innerHTML]` sanitizer
|
|
5
|
+
* (which strips ALL SVG and inline styles). Configured for the HTML + SVG
|
|
6
|
+
* profiles, DOMPurify keeps safe structural HTML **and** inline vector graphics
|
|
7
|
+
* (`<svg>`, `<path>`, gradients, `<text>`, …) and inline `style`, while removing
|
|
8
|
+
* the dangerous vectors: `<script>`, `on*` event handlers, `javascript:`/`data:`
|
|
9
|
+
* script URLs, `<foreignObject>`, and friends.
|
|
10
|
+
*
|
|
11
|
+
* Because DOMPurify has already neutralized the markup, a caller may then bind
|
|
12
|
+
* the result via `DomSanitizer.bypassSecurityTrustHtml(...)` — the documented
|
|
13
|
+
* safe pattern (sanitize first, then bypass Angular's redundant re-sanitize).
|
|
14
|
+
* The {@link MJSafeRichHtmlPipe} pipe does exactly that; prefer it in templates.
|
|
15
|
+
*
|
|
16
|
+
* Safe for untrusted input by design, so it suits both explicitly-typed HTML
|
|
17
|
+
* fields and content that was only heuristically detected as HTML.
|
|
18
|
+
*
|
|
19
|
+
* @param raw the raw HTML string (null/undefined → '')
|
|
20
|
+
* @returns a sanitized HTML string safe to render
|
|
21
|
+
*/
|
|
22
|
+
export declare function PurifyRichTextHtml(raw: string | null | undefined): string;
|
|
23
|
+
//# sourceMappingURL=rich-text-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-text-sanitize.d.ts","sourceRoot":"","sources":["../../../src/lib/rich-text/rich-text-sanitize.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAIzE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import DOMPurify from 'dompurify';
|
|
2
|
+
/**
|
|
3
|
+
* Sanitize untrusted HTML for safe rich-text rendering.
|
|
4
|
+
*
|
|
5
|
+
* Use this instead of relying on Angular's built-in `[innerHTML]` sanitizer
|
|
6
|
+
* (which strips ALL SVG and inline styles). Configured for the HTML + SVG
|
|
7
|
+
* profiles, DOMPurify keeps safe structural HTML **and** inline vector graphics
|
|
8
|
+
* (`<svg>`, `<path>`, gradients, `<text>`, …) and inline `style`, while removing
|
|
9
|
+
* the dangerous vectors: `<script>`, `on*` event handlers, `javascript:`/`data:`
|
|
10
|
+
* script URLs, `<foreignObject>`, and friends.
|
|
11
|
+
*
|
|
12
|
+
* Because DOMPurify has already neutralized the markup, a caller may then bind
|
|
13
|
+
* the result via `DomSanitizer.bypassSecurityTrustHtml(...)` — the documented
|
|
14
|
+
* safe pattern (sanitize first, then bypass Angular's redundant re-sanitize).
|
|
15
|
+
* The {@link MJSafeRichHtmlPipe} pipe does exactly that; prefer it in templates.
|
|
16
|
+
*
|
|
17
|
+
* Safe for untrusted input by design, so it suits both explicitly-typed HTML
|
|
18
|
+
* fields and content that was only heuristically detected as HTML.
|
|
19
|
+
*
|
|
20
|
+
* @param raw the raw HTML string (null/undefined → '')
|
|
21
|
+
* @returns a sanitized HTML string safe to render
|
|
22
|
+
*/
|
|
23
|
+
export function PurifyRichTextHtml(raw) {
|
|
24
|
+
return DOMPurify.sanitize(raw ?? '', {
|
|
25
|
+
USE_PROFILES: { html: true, svg: true, svgFilters: true },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=rich-text-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-text-sanitize.js","sourceRoot":"","sources":["../../../src/lib/rich-text/rich-text-sanitize.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B;IAC7D,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,EAAE,EAAE;QACjC,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE;KAC5D,CAAC,CAAC;AACP,CAAC","sourcesContent":["import DOMPurify from 'dompurify';\n\n/**\n * Sanitize untrusted HTML for safe rich-text rendering.\n *\n * Use this instead of relying on Angular's built-in `[innerHTML]` sanitizer\n * (which strips ALL SVG and inline styles). Configured for the HTML + SVG\n * profiles, DOMPurify keeps safe structural HTML **and** inline vector graphics\n * (`<svg>`, `<path>`, gradients, `<text>`, …) and inline `style`, while removing\n * the dangerous vectors: `<script>`, `on*` event handlers, `javascript:`/`data:`\n * script URLs, `<foreignObject>`, and friends.\n *\n * Because DOMPurify has already neutralized the markup, a caller may then bind\n * the result via `DomSanitizer.bypassSecurityTrustHtml(...)` — the documented\n * safe pattern (sanitize first, then bypass Angular's redundant re-sanitize).\n * The {@link MJSafeRichHtmlPipe} pipe does exactly that; prefer it in templates.\n *\n * Safe for untrusted input by design, so it suits both explicitly-typed HTML\n * fields and content that was only heuristically detected as HTML.\n *\n * @param raw the raw HTML string (null/undefined → '')\n * @returns a sanitized HTML string safe to render\n */\nexport function PurifyRichTextHtml(raw: string | null | undefined): string {\n return DOMPurify.sanitize(raw ?? '', {\n USE_PROFILES: { html: true, svg: true, svgFilters: true },\n });\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PipeTransform } from '@angular/core';
|
|
2
|
+
import { SafeHtml } from '@angular/platform-browser';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Renders untrusted HTML safely — keeping SVG and richer markup — for use with
|
|
6
|
+
* `[innerHTML]`. Sanitizes with DOMPurify (HTML + SVG profiles) and then wraps
|
|
7
|
+
* the cleaned string with `bypassSecurityTrustHtml`, so Angular doesn't strip
|
|
8
|
+
* the now-safe SVG/styles a second time.
|
|
9
|
+
*
|
|
10
|
+
* Pure pipe → Angular memoizes by input value, so it only re-sanitizes when the
|
|
11
|
+
* string actually changes (cheap under OnPush).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```html
|
|
15
|
+
* <div [innerHTML]="someHtmlString | mjSafeRichHtml"></div>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare class MJSafeRichHtmlPipe implements PipeTransform {
|
|
19
|
+
private sanitizer;
|
|
20
|
+
transform(value: string | null | undefined): SafeHtml;
|
|
21
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MJSafeRichHtmlPipe, never>;
|
|
22
|
+
static ɵpipe: i0.ɵɵPipeDeclaration<MJSafeRichHtmlPipe, "mjSafeRichHtml", true>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=safe-rich-html.pipe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-rich-html.pipe.d.ts","sourceRoot":"","sources":["../../../src/lib/rich-text/safe-rich-html.pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,aAAa,EAAU,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAgB,QAAQ,EAAE,MAAM,2BAA2B,CAAC;;AAGnE;;;;;;;;;;;;;GAaG;AACH,qBAKa,kBAAmB,YAAW,aAAa;IACpD,OAAO,CAAC,SAAS,CAAwB;IAEzC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ;yCAH5C,kBAAkB;uCAAlB,kBAAkB;CAM9B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Pipe, inject } from '@angular/core';
|
|
2
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
3
|
+
import { PurifyRichTextHtml } from './rich-text-sanitize';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Renders untrusted HTML safely — keeping SVG and richer markup — for use with
|
|
7
|
+
* `[innerHTML]`. Sanitizes with DOMPurify (HTML + SVG profiles) and then wraps
|
|
8
|
+
* the cleaned string with `bypassSecurityTrustHtml`, so Angular doesn't strip
|
|
9
|
+
* the now-safe SVG/styles a second time.
|
|
10
|
+
*
|
|
11
|
+
* Pure pipe → Angular memoizes by input value, so it only re-sanitizes when the
|
|
12
|
+
* string actually changes (cheap under OnPush).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```html
|
|
16
|
+
* <div [innerHTML]="someHtmlString | mjSafeRichHtml"></div>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export class MJSafeRichHtmlPipe {
|
|
20
|
+
sanitizer = inject(DomSanitizer);
|
|
21
|
+
transform(value) {
|
|
22
|
+
return this.sanitizer.bypassSecurityTrustHtml(PurifyRichTextHtml(value));
|
|
23
|
+
}
|
|
24
|
+
static ɵfac = function MJSafeRichHtmlPipe_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJSafeRichHtmlPipe)(); };
|
|
25
|
+
static ɵpipe = /*@__PURE__*/ i0.ɵɵdefinePipe({ name: "mjSafeRichHtml", type: MJSafeRichHtmlPipe, pure: true });
|
|
26
|
+
}
|
|
27
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJSafeRichHtmlPipe, [{
|
|
28
|
+
type: Pipe,
|
|
29
|
+
args: [{
|
|
30
|
+
name: 'mjSafeRichHtml',
|
|
31
|
+
standalone: true,
|
|
32
|
+
pure: true,
|
|
33
|
+
}]
|
|
34
|
+
}], null, null); })();
|
|
35
|
+
//# sourceMappingURL=safe-rich-html.pipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-rich-html.pipe.js","sourceRoot":"","sources":["../../../src/lib/rich-text/safe-rich-html.pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAiB,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;;AAE1D;;;;;;;;;;;;;GAaG;AAMH,MAAM,OAAO,kBAAkB;IACnB,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAEzC,SAAS,CAAC,KAAgC;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC;4GALQ,kBAAkB;iFAAlB,kBAAkB;;iFAAlB,kBAAkB;cAL9B,IAAI;eAAC;gBACF,IAAI,EAAE,gBAAgB;gBACtB,UAAU,EAAE,IAAI;gBAChB,IAAI,EAAE,IAAI;aACb","sourcesContent":["import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { PurifyRichTextHtml } from './rich-text-sanitize';\n\n/**\n * Renders untrusted HTML safely — keeping SVG and richer markup — for use with\n * `[innerHTML]`. Sanitizes with DOMPurify (HTML + SVG profiles) and then wraps\n * the cleaned string with `bypassSecurityTrustHtml`, so Angular doesn't strip\n * the now-safe SVG/styles a second time.\n *\n * Pure pipe → Angular memoizes by input value, so it only re-sanitizes when the\n * string actually changes (cheap under OnPush).\n *\n * @example\n * ```html\n * <div [innerHTML]=\"someHtmlString | mjSafeRichHtml\"></div>\n * ```\n */\n@Pipe({\n name: 'mjSafeRichHtml',\n standalone: true,\n pure: true,\n})\nexport class MJSafeRichHtmlPipe implements PipeTransform {\n private sanitizer = inject(DomSanitizer);\n\n transform(value: string | null | undefined): SafeHtml {\n return this.sanitizer.bypassSecurityTrustHtml(PurifyRichTextHtml(value));\n }\n}\n"]}
|
package/dist/public-api.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export * from './lib/module';
|
|
|
2
2
|
export * from './lib/recent-access.service';
|
|
3
3
|
export * from './lib/theme.service';
|
|
4
4
|
export * from './lib/loading/loading.component';
|
|
5
|
+
export * from './lib/rich-text/rich-text-sanitize';
|
|
6
|
+
export * from './lib/rich-text/safe-rich-html.pipe';
|
|
5
7
|
//# sourceMappingURL=public-api.d.ts.map
|
package/dist/public-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAKA,cAAc,cAAc,CAAC;AAG7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AAGpC,cAAc,iCAAiC,CAAC"}
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAKA,cAAc,cAAc,CAAC;AAG7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AAGpC,cAAc,iCAAiC,CAAC;AAGhD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC"}
|
package/dist/public-api.js
CHANGED
|
@@ -8,4 +8,7 @@ export * from './lib/recent-access.service';
|
|
|
8
8
|
export * from './lib/theme.service';
|
|
9
9
|
// Components
|
|
10
10
|
export * from './lib/loading/loading.component';
|
|
11
|
+
// Rich-text rendering (safe HTML/SVG sanitization)
|
|
12
|
+
export * from './lib/rich-text/rich-text-sanitize';
|
|
13
|
+
export * from './lib/rich-text/safe-rich-html.pipe';
|
|
11
14
|
//# sourceMappingURL=public-api.js.map
|
package/dist/public-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,cAAc,cAAc,CAAC;AAE7B,WAAW;AACX,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AAEpC,aAAa;AACb,cAAc,iCAAiC,CAAC","sourcesContent":["/*\n * Public API Surface for @memberjunction/ng-shared-generic\n */\n\n// Module\nexport * from './lib/module';\n\n// Services\nexport * from './lib/recent-access.service';\nexport * from './lib/theme.service';\n\n// Components\nexport * from './lib/loading/loading.component';\n"]}
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,cAAc,cAAc,CAAC;AAE7B,WAAW;AACX,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AAEpC,aAAa;AACb,cAAc,iCAAiC,CAAC;AAEhD,mDAAmD;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC","sourcesContent":["/*\n * Public API Surface for @memberjunction/ng-shared-generic\n */\n\n// Module\nexport * from './lib/module';\n\n// Services\nexport * from './lib/recent-access.service';\nexport * from './lib/theme.service';\n\n// Components\nexport * from './lib/loading/loading.component';\n\n// Rich-text rendering (safe HTML/SVG sanitization)\nexport * from './lib/rich-text/rich-text-sanitize';\nexport * from './lib/rich-text/safe-rich-html.pipe';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-shared-generic",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.39.0",
|
|
4
4
|
"description": "MemberJunction: Generic Angular Shared Package - utility services and reusable elements used in any Angular application.",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"/dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "
|
|
11
|
+
"test": "vitest run",
|
|
12
12
|
"build": "npm run clean && ngc && npm run copy-assets",
|
|
13
13
|
"copy-assets": "cpy 'src/lib/_tokens.scss' dist/lib --flat",
|
|
14
14
|
"clean": "rimraf dist",
|
|
@@ -28,13 +28,15 @@
|
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@angular/common": "21.1.3",
|
|
30
30
|
"@angular/core": "21.1.3",
|
|
31
|
+
"@angular/platform-browser": "21.1.3",
|
|
31
32
|
"rxjs": "^7.8.2"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@memberjunction/core": "5.
|
|
35
|
-
"@memberjunction/ng-base-types": "5.
|
|
36
|
-
"@memberjunction/core-entities": "5.
|
|
37
|
-
"@memberjunction/global": "5.
|
|
35
|
+
"@memberjunction/core": "5.39.0",
|
|
36
|
+
"@memberjunction/ng-base-types": "5.39.0",
|
|
37
|
+
"@memberjunction/core-entities": "5.39.0",
|
|
38
|
+
"@memberjunction/global": "5.39.0",
|
|
39
|
+
"dompurify": "^3.3.0",
|
|
38
40
|
"tslib": "^2.8.1"
|
|
39
41
|
},
|
|
40
42
|
"sideEffects": false,
|