spyne 0.22.3 → 0.23.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Spyne 0.22.3
2
+ * Spyne 0.23.0
3
3
  * https://spynejs.org
4
4
  *
5
5
  * @license
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  /*!
18
- * spynejs 0.22.3
18
+ * spynejs 0.23.0
19
19
  * https://spynejs.org
20
20
  * (c) 2017-present Frank Batista
21
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spyne",
3
- "version": "0.22.3",
3
+ "version": "0.23.0",
4
4
  "description": "Reactive Real-DOM Framework for Advanced Javascript applications",
5
5
  "sideEffects": true,
6
6
  "main": "./lib/spyne.esm.js",
@@ -46,7 +46,7 @@ export class ChannelFetch extends Channel {
46
46
  *
47
47
  */
48
48
 
49
- constructor(name, props = {}) {
49
+ constructor(name, props = { disableSanitize:false }) {
50
50
  // ALLOW FOR GENERIC MAP PROPERTY
51
51
 
52
52
  ChannelFetch.validateMapMethod(props, name)
@@ -123,7 +123,7 @@ export class ChannelFetch extends Channel {
123
123
  }
124
124
 
125
125
  consolidateAllFetchProps(options, props = this.props) {
126
- const propsOptions = pick(['mapFn', 'url', 'header', 'body', 'mode', 'method', 'responseType', 'debug'], props)
126
+ const propsOptions = pick(['mapFn', 'url', 'header', 'body', 'mode', 'disableSanitize', 'method', 'responseType', 'debug'], props)
127
127
  const mergeOptions = (o1, o2) => mergeDeepRight(o1, o2)
128
128
  const filterOutUndefined = reject(isNil)
129
129
  return compose(filterOutUndefined, mergeOptions)(propsOptions, options)
@@ -6,7 +6,7 @@ import { sanitizeHTMLConfigure } from './utils/sanitize-html.js'
6
6
  import { sanitizeDataConfigure } from './utils/sanitize-data.js'
7
7
 
8
8
  const _channels = new ChannelsMap()
9
- const version = '0.22.3'
9
+ const version = '0.23.0'
10
10
 
11
11
  class SpyneApplication {
12
12
  /**
@@ -41,7 +41,7 @@ class SpyneApplication {
41
41
  init(config = {}, testMode = false) {
42
42
  // this.channels = new ChannelsMap();
43
43
  /*!
44
- * Spyne 0.22.3
44
+ * Spyne 0.23.0
45
45
  * https://spynejs.org
46
46
  *
47
47
  * @license
@@ -59,6 +59,7 @@ export class ChannelFetchUtil {
59
59
  this._serverOptions = ChannelFetchUtil.setServerOptions(options)
60
60
  this._subscriber = subscriber !== undefined ? subscriber : testSubscriber
61
61
  this.debug = options.debug !== undefined ? options.debug : false
62
+ this.disableSanitize = options?.disableSanitize
62
63
  this.channelName = CHANNEL_NAME
63
64
 
64
65
  const fetchProps = {
@@ -66,6 +67,7 @@ export class ChannelFetchUtil {
66
67
  url: this.url,
67
68
  serverOptions: this.serverOptions,
68
69
  responseType: this.responseType,
70
+ disableSanitize: this.disableSanitize,
69
71
  debug: this.debug
70
72
  }
71
73
  if (testMode !== true) {
@@ -82,8 +84,8 @@ export class ChannelFetchUtil {
82
84
  const metadata = { channelName, url, responseType, serverOptions }
83
85
 
84
86
  return (data) => {
85
- const disableSanitize = props?.disableSanitize === true
86
-
87
+ const disableSanitize = props?.disableSanitize === true
88
+ /*
87
89
  if (disableSanitize) {
88
90
  const env = (typeof process !== 'undefined' && process.env && process.env.NODE_ENV)
89
91
  ? process.env.NODE_ENV
@@ -100,6 +102,7 @@ export class ChannelFetchUtil {
100
102
  )
101
103
  }
102
104
  }
105
+ */
103
106
 
104
107
  const sanitizedData = disableSanitize ? data : sanitizeData(data)
105
108
  return mapMethod(sanitizedData, metadata)
@@ -3,25 +3,77 @@ import DOMPurify from 'dompurify'
3
3
  let _sanitizeHTML
4
4
  let isConfigured = false
5
5
 
6
- const sanitizeHTMLConfigure = (config) => {
7
- if (!isConfigured) {
8
- if (config.strict) {
9
- if (window.trustedTypes) {
10
- const policy = window.trustedTypes.createPolicy('default', {
11
- createHTML: (to_escape) => DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false })
12
- })
13
-
14
- _sanitizeHTML = (html) => policy.createHTML(html)
15
- } else {
16
- _sanitizeHTML = (html) => DOMPurify.sanitize(html)
17
- }
18
- } else {
19
- _sanitizeHTML = (html) => html // No sanitization if not required
20
- }
6
+ const sanitizeHTMLConfigure = (config = {}) => {
7
+ if (isConfigured) {
8
+ console.warn('sanitizeHTML is already configured. Reconfiguration is not allowed.')
9
+ return
10
+ }
11
+
12
+ const {
13
+ strict = true,
14
+ allowTargetAttr = true,
15
+ addNoopener = true
16
+ } = config
17
+
18
+ if (!strict) {
19
+ _sanitizeHTML = (html) => html
21
20
  isConfigured = true
21
+ return
22
+ }
23
+
24
+ const domPurifyConfig = {
25
+ RETURN_TRUSTED_TYPE: false,
26
+ ADD_ATTR: allowTargetAttr ? ['target'] : []
27
+ }
28
+
29
+ const sanitizeWithPolicy = (html) => DOMPurify.sanitize(html, domPurifyConfig)
30
+
31
+ if (allowTargetAttr) {
32
+ DOMPurify.addHook('afterSanitizeAttributes', (node) => {
33
+ if (!node || typeof node.getAttribute !== 'function') return
34
+ if (node.tagName !== 'A') return
35
+
36
+ const href = node.getAttribute('href')
37
+ const target = node.getAttribute('target')
38
+
39
+ // remove target on anchors without href
40
+ if (!href && target) {
41
+ node.removeAttribute('target')
42
+ return
43
+ }
44
+
45
+ if (!target) return
46
+
47
+ const validTargets = ['_blank', '_self', '_parent', '_top']
48
+ if (!validTargets.includes(target)) {
49
+ node.removeAttribute('target')
50
+ return
51
+ }
52
+
53
+ if (target === '_blank' && addNoopener) {
54
+ const currentRel = node.getAttribute('rel') || ''
55
+ const relParts = currentRel.split(/\s+/).filter(Boolean)
56
+ const relSet = new Set(relParts)
57
+
58
+ relSet.add('noopener')
59
+ relSet.add('noreferrer')
60
+
61
+ node.setAttribute('rel', Array.from(relSet).join(' '))
62
+ }
63
+ })
64
+ }
65
+
66
+ if (window.trustedTypes) {
67
+ const policy = window.trustedTypes.createPolicy('default', {
68
+ createHTML: (toEscape) => sanitizeWithPolicy(toEscape)
69
+ })
70
+
71
+ _sanitizeHTML = (html) => policy.createHTML(html)
22
72
  } else {
23
- console.warn('sanitizeHTML is already configured. Reconfiguration is not allowed.')
73
+ _sanitizeHTML = (html) => sanitizeWithPolicy(html)
24
74
  }
75
+
76
+ isConfigured = true
25
77
  }
26
78
 
27
79
  const sanitizeHTML = (html) => {
@@ -1,5 +1,6 @@
1
1
  import { getAllMethodNames } from './frp-tools.js'
2
2
  import { reject, curryN, __, map } from 'ramda'
3
+ import { SpyneAppProperties } from './spyne-app-properties.js'
3
4
 
4
5
  export class SpyneTrait {
5
6
  /**
@@ -44,7 +45,9 @@ export class SpyneTrait {
44
45
  }
45
46
 
46
47
  checkForMalformedMethods(methodsArr) {
47
- if (this.prefix === '') {
48
+ const isDebugMode = SpyneAppProperties?.debug || false
49
+
50
+ if (this.prefix === '' && isDebugMode) {
48
51
  console.warn(`SPYNE WARNING: The following SpyneTrait ${this.constructor.name} needs a prefix`)
49
52
  return
50
53
  }
@@ -53,7 +56,7 @@ export class SpyneTrait {
53
56
  const hasPrefix = (str) => str.indexOf(this.prefix) === 0
54
57
 
55
58
  const malformedMethodsArr = reject(hasPrefix, methodsArr)
56
- if (malformedMethodsArr.length >= 1) {
59
+ if (malformedMethodsArr.length >= 1 && isDebugMode) {
57
60
  const warningStr = `Spyne Warning: The following method(s) in ${this.constructor.name} require the prefix, "${this.prefix}": [${malformedMethodsArr.join(', ')}];`
58
61
  console.warn(warningStr)
59
62
  }
@@ -51,10 +51,12 @@ export class DomElementTemplate {
51
51
  this.template = DomElementTemplate.normalizeTripleBrackets(this.template)
52
52
 
53
53
  if (this.isProxyData === true) {
54
- if (SpyneAppProperties.enableCMSProxies === true) {
55
- this.template = SpyneAppProperties.formatTemplateForProxyData(this.template)
56
- } else {
57
- this.template = DomElementTemplate.formatTemplateForProxyData(this.template)
54
+ const proxyFormatter = SpyneAppProperties.enableCMSProxies === true
55
+ ? SpyneAppProperties.formatTemplateForProxyData
56
+ : DomElementTemplate.formatTemplateForProxyData
57
+
58
+ if (typeof proxyFormatter === 'function') {
59
+ this.template = proxyFormatter(this.template)
58
60
  }
59
61
  }
60
62
 
@@ -85,6 +85,10 @@ export class ViewStreamBroadcaster {
85
85
  const data = {}// convertDomStringMapToObj(q.dataset);
86
86
  data.payload = convertDomStringMapToObj(q.dataset)
87
87
  data.payload = omit(['channel'], data.payload)
88
+ if (data.channel === 'ROUTE') {
89
+ data.payload.endPreventDefault = true
90
+ }
91
+
88
92
  data.channel = channel
89
93
  // payload needs vsid# to pass verification
90
94