@concretecms/bedrock 1.6.4 → 1.6.5

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.
@@ -0,0 +1,220 @@
1
+ ;(function(global) {
2
+ 'use strict'
3
+
4
+ /**
5
+ * Recursively add fields to URLSearchParams.
6
+ * Used by buildRequestBody().
7
+ *
8
+ * @param {string} prefix
9
+ * @param {Record|Array|any} value
10
+ * @param {URLSearchParams} urlSearchParams
11
+ *
12
+ * @returns {void}
13
+ */
14
+ function addToUrlSearchParams(prefix, value, urlSearchParams) {
15
+ if (value === null || value === undefined || typeof value !== 'object') {
16
+ return
17
+ }
18
+ if (!prefix && Array.isArray(value)) {
19
+ return
20
+ }
21
+ for (const [key, val] of Object.entries(value)) {
22
+ if (val === null || val === undefined) {
23
+ continue
24
+ }
25
+ const fieldName = prefix ? `${prefix}[${key}]` : key
26
+ if (typeof val === 'object') {
27
+ addToUrlSearchParams(fieldName, val, urlSearchParams)
28
+ } else {
29
+ urlSearchParams.append(fieldName, String(val))
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Build the request body for an AJAX request.
36
+ *
37
+ * @param {Record|any} data The object to build the body from
38
+ *
39
+ * @returns {URLSearchParams} The request body
40
+ *
41
+ * @example
42
+ * buildRequestBody({key: 'value', arr: [1, 2, 3], nested: {a: 'b'}})
43
+ */
44
+ function buildRequestBody(data) {
45
+ const urlSearchParams = new URLSearchParams()
46
+ addToUrlSearchParams('', data, urlSearchParams)
47
+ return urlSearchParams
48
+ }
49
+
50
+ /**
51
+ * Prepare the request options for window.fetch().
52
+ *
53
+ * @param {RequestInit|Record<string, any>|null|undefined} request
54
+ * @param {Record<string, string>|null|undefined} headers Additional headers to add (will not override existing ones)
55
+ *
56
+ * @returns {RequestInit}
57
+ */
58
+ function prepareRequest(request, headers) {
59
+ if (request) {
60
+ try {
61
+ request = global.structuredClone(request)
62
+ } catch {}
63
+ } else {
64
+ request = {}
65
+ }
66
+ // Default to GET if no method is specified
67
+ request.method = String(request.method || 'GET').toUpperCase()
68
+ if (request.body?.constructor === Object) {
69
+ // Convert body object to URLSearchParams
70
+ request.body = buildRequestBody(request.body)
71
+ }
72
+ if (!request.headers) {
73
+ request.headers = {}
74
+ }
75
+ if (!request.cache && request.method !== 'GET') {
76
+ // Disable caching for non-GET requests by default
77
+ request.cache = 'no-cache'
78
+ }
79
+ const existingHeaderKeys = Object.keys(request.headers).map((key) => key.toLowerCase())
80
+ if (headers) {
81
+ for (const [name, value] of Object.entries(headers)) {
82
+ const lowerCaseName = name.toLowerCase()
83
+ if (!existingHeaderKeys.includes(lowerCaseName)) {
84
+ request.headers[name] = value
85
+ existingHeaderKeys.push(lowerCaseName)
86
+ }
87
+ }
88
+ }
89
+ if (!existingHeaderKeys.includes('x-requested-with')) {
90
+ // Just to let the server know this is an AJAX request
91
+ request.headers['X-Requested-With'] = 'XMLHttpRequest'
92
+ }
93
+ if (request.method !== 'GET' && request.body && !existingHeaderKeys.includes('content-type')) {
94
+ // We want to use $_POST on the server side
95
+ request.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
96
+ }
97
+ return request
98
+ }
99
+
100
+ /**
101
+ * Check a JSON response for errors.
102
+ *
103
+ * @param {any} responseData
104
+ *
105
+ * @throws {Error} If the response data contains errors (the thrown error will have a responseData property)
106
+ */
107
+ function checkJsonResponse(responseData) {
108
+ if (responseData?.errors?.length) {
109
+ const error = new Error(responseData.errors[0])
110
+ error.responseData = responseData
111
+ throw error
112
+ }
113
+ if (responseData?.error) {
114
+ const error = new Error(responseData.error)
115
+ error.responseData = responseData
116
+ throw error
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Fetch JSON data from a URL.
122
+ *
123
+ * @param {string} url The URL to fetch data from
124
+ * @param {RequestInit|Record<string, any>|null|undefined} request The request options and body
125
+ *
126
+ * @throws {Error} If the response contains an error or is not ok
127
+ *
128
+ * @returns {Promise<any>} The JSON response
129
+ *
130
+ * @example
131
+ * try {
132
+ * const data = await fetchJson('/api/data', {
133
+ * method: 'POST',
134
+ * body: {
135
+ * key: 'value'
136
+ * }
137
+ * });
138
+ * } catch (error) {
139
+ * window.alert(error.message);
140
+ * }
141
+ */
142
+ async function fetchJson(url, request) {
143
+ request = prepareRequest(request, { Accept: 'application/json' })
144
+ const response = await fetch(url, request)
145
+ const responseText = await response.text()
146
+ let responseData
147
+ try {
148
+ responseData = JSON.parse(responseText)
149
+ } catch {
150
+ throw new Error(responseText)
151
+ }
152
+ checkJsonResponse(responseData)
153
+ if (!response.ok) {
154
+ throw new Error(responseText)
155
+ }
156
+ return responseData
157
+ }
158
+
159
+ /**
160
+ * Fetch an HTML chunk from a URL.
161
+ *
162
+ * @param {string} url The URL to fetch data from
163
+ * @param {RequestInit|Record<string, any>|undefined} request The request options and body
164
+ *
165
+ * @throws {Error} If the response contains an error or is not ok
166
+ *
167
+ * @returns {Promise<string>} The HTML response
168
+ *
169
+ * @example
170
+ * try {
171
+ * const data = await fetchHtml('/api/render', {
172
+ * method: 'POST',
173
+ * body: {
174
+ * key: 'value'
175
+ * }
176
+ * });
177
+ * } catch (error) {
178
+ * window.alert(error.message);
179
+ * }
180
+ */
181
+ async function fetchHtml(url, request) {
182
+ request = prepareRequest(
183
+ request,
184
+ {
185
+ Accept: [
186
+ // Prefer HTML
187
+ 'text/html',
188
+ // ... but accept JSON in case of errors
189
+ 'application/json;q=0.9',
190
+ // ... or plain text as a last resort fallback
191
+ 'text/plain;q=0.8'
192
+ ].join(', ')
193
+ }
194
+ )
195
+ const response = await fetch(url, request)
196
+ const responseText = await response.text()
197
+ let responseData
198
+ try {
199
+ // Try to see if it's JSON with errors
200
+ responseData = JSON.parse(responseText)
201
+ } catch {
202
+ // Not JSON, that's fine
203
+ responseData = null
204
+ }
205
+ if (responseData) {
206
+ checkJsonResponse(responseData)
207
+ }
208
+
209
+ if (!response.ok) {
210
+ throw new Error(responseText)
211
+ }
212
+ return responseText
213
+ }
214
+
215
+ global.ConcreteFetch = {
216
+ buildRequestBody,
217
+ json: fetchJson,
218
+ html: fetchHtml
219
+ }
220
+ })(global)
@@ -59,6 +59,24 @@ export default class Manager {
59
59
  }, 10)
60
60
  }
61
61
 
62
+ /**
63
+ * Activates a particular context (and its components) for a particular selector, returning a promise.
64
+ *
65
+ * @param {String} context
66
+ *
67
+ * @returns {Promise<{Vue: typeof Vue, options: Record<string, any>}>
68
+ *
69
+ * @example
70
+ * const {Vue, options} = await Concrete.Vue.activateContextAsync('cms')
71
+ */
72
+ activateContextAsync(context) {
73
+ return new Promise((resolve) => {
74
+ this.activateContext(context, (Vue, options) => {
75
+ resolve({ Vue, options })
76
+ })
77
+ })
78
+ }
79
+
62
80
  /**
63
81
  * For a given string `context`, adds the passed components to make them available within that context.
64
82
  *
@@ -9,4 +9,6 @@
9
9
  @import 'bootstrap/scss/mixins';
10
10
  @import '../../cms/scss/variables';
11
11
 
12
+ @import 'dropzone/dist/dropzone.css';
13
+
12
14
  @import 'frontend/frontend';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@concretecms/bedrock",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "description": "The asset framework and dependencies for Concrete CMS.",
5
5
  "scripts": {
6
6
  "lint": "standardx \"**/*.{js,vue}\" && stylelint assets/**/*.{scss,vue}",