druxt 0.24.0 → 0.24.1-dev.20260920063819

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/README.md CHANGED
@@ -1,36 +1,131 @@
1
- # DruxtJS; The Fully Decoupled Drupal Framework.
1
+ <img src="./banner.svg" alt="Druxt: the core client, store and DruxtModule base">
2
+
3
+ # Druxt
2
4
 
3
5
  [![npm](https://badgen.net/npm/v/druxt)](https://www.npmjs.com/package/druxt)
4
- [![CircleCI](https://circleci.com/gh/druxt/druxt.js.svg?style=svg)](https://circleci.com/gh/druxt/druxt.js)
6
+ [![CI](https://github.com/druxt/druxt.js/actions/workflows/ci.yml/badge.svg)](https://github.com/druxt/druxt.js/actions/workflows/ci.yml)
5
7
  [![Known Vulnerabilities](https://snyk.io/test/github/druxt/druxt.js/badge.svg?targetFile=package.json)](https://snyk.io/test/github/druxt/druxt.js?targetFile=package.json)
6
8
  [![codecov](https://codecov.io/gh/druxt/druxt.js/branch/develop/graph/badge.svg)](https://codecov.io/gh/druxt/druxt.js)
7
9
 
10
+ > The core module, DruxtClient, DruxtStore and the DruxtModule base component.
8
11
 
9
- > Druxt = DRUpal + nUXT = Fully Decoupled Drupal.
12
+ The core `druxt` package is the foundation every other Druxt module builds
13
+ on. It provides the three pieces the whole framework shares:
10
14
 
11
- * * *
15
+ - **[DruxtClient](#druxtclient)**: the Drupal JSON:API communication layer.
16
+ - **[DruxtStore](#druxtstore)**: the Vuex module caching resources and
17
+ collections for all modules.
18
+ - **[DruxtModule](#druxtmodule)**: the base component behind the
19
+ theme-component suggestion system.
12
20
 
13
- ## Links
21
+ ## Installation
14
22
 
15
- - Documentation: https://druxtjs.org
16
- - Community Discord server: https://discord.druxtjs.org
17
- - Demo - Umami Food Magazine:
18
- - https://demo.druxtjs.org
19
- - https://druxt-umami.netlify.app
20
- - https://umami-storybook.druxtjs.org
23
+ 1. Install the package:
24
+
25
+ ```sh
26
+ npm i druxt
27
+ ```
28
+
29
+ 2. Add the module and your backend URL to `nuxt.config.js`:
30
+
31
+ ```js
32
+ export default {
33
+ modules: [['druxt', { baseUrl: 'https://api.umami.demo.druxtjs.org' }]],
34
+ };
35
+ ```
36
+
37
+ \* _Replace `https://api.umami.demo.druxtjs.org` with your own
38
+ Drupal backend._
39
+
40
+ 3. On the Drupal side, install and enable the
41
+ [Druxt module](https://www.drupal.org/project/druxt) and grant the
42
+ **access druxt resources** permission to the relevant roles. Missing this
43
+ is the most common cause of site-wide JSON:API failures. See
44
+ [Troubleshooting](https://druxtjs.org/how-to/troubleshooting)
45
+ if requests are failing after install.
46
+
47
+ ## Compatibility
48
+
49
+ As of September 2026. See the [releases](https://github.com/druxt/druxt.js/releases) for the current version.
50
+
51
+ | | Supported |
52
+ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | Nuxt | 2.15 or later (Nuxt 2 line). Nuxt 3 is not supported. |
54
+ | Vue | 2.7 |
55
+ | Node | 16, the tested version. Node 16 is past upstream end of life. The 1.x line targets it deliberately. 17 and later need `NODE_OPTIONS=--openssl-legacy-provider` to build. |
56
+ | Drupal core | 10 or 11. `drupal/druxt` 1.2.2 declares `^10 \|\| ^11 \|\| ^12` and ended 8 and 9 support. 12 is declared but waits on Decoupled Router and JSON:API Menu Items. Tested against 10 and 11. |
57
+ | Drupal module | `drupal/druxt` ^1.2.2, which brings `decoupled_router`, `jsonapi_menu_items` and `jsonapi_views`. Earlier 1.2.x releases are incompatible with Decoupled Router 2.0.7 ([#3618675](https://www.drupal.org/i/3618675)). |
58
+ | Known pins | `@nuxtjs/storybook` 4.2.0 (later majors need Nuxt 3). Axios 0.x, or add it to `build.transpile` |
59
+
60
+ What these bounds promise over time is stated in the
61
+ [support and versioning policy](https://druxtjs.org/explanation/support-and-versioning).
62
+
63
+ Translated routes also need a `decoupled_router` patch; see the
64
+ [multilingual guide](https://druxtjs.org/how-to/multilingual).
65
+
66
+ ## Settings
67
+
68
+ | Option | Type | Default | Description |
69
+ | ------------- | ----------------- | ------------ | ------------------------------------------------------------------------ |
70
+ | `baseUrl` | `string` | | The Drupal backend URL. **Required.** |
71
+ | `endpoint` | `string` | `'/jsonapi'` | The JSON:API endpoint path. |
72
+ | `proxy.api` | `boolean` | `false` | Proxy API requests via Nuxt ([guide](https://druxtjs.org/how-to/proxy)). |
73
+ | `proxy.files` | `boolean\|string` | `false` | Proxy Drupal files. A string sets the site. |
74
+
75
+ See the [Nuxt module API](https://druxtjs.org/api/packages/druxt/nuxt) for the full
76
+ options list.
77
+
78
+ ## DruxtClient
21
79
 
22
- * * *
80
+ The client is the communication layer between your application and Drupal's
81
+ JSON:API: resources, collections, indexing and authentication all flow
82
+ through it.
23
83
 
24
- ## Features
84
+ ```js
85
+ import { DruxtClient } from 'druxt';
25
86
 
26
- - Fully Decoupled Drupal, with [Nuxt.js](https://github.com/nuxt/nuxt.js#features) in the frontend.
27
- - Drupal JSON:API Client with Vuex caching.
28
- - Modular Vue.js component library system.
29
- - Slot and Wrapper theming system.
87
+ const client = new DruxtClient('https://api.umami.demo.druxtjs.org');
88
+ const collection = await client.getCollection('node--article');
89
+ ```
30
90
 
31
- Learn more at https://druxtjs.org
91
+ - [Use the Druxt client directly](https://druxtjs.org/how-to/use-the-druxt-client): practical
92
+ guide, including non-Nuxt usage.
93
+ - [DruxtClient API](https://druxtjs.org/api/packages/druxt/client): full method reference.
32
94
 
33
- * * *
95
+ ## DruxtStore
96
+
97
+ The Vuex module that all Druxt modules share: request deduplication, the
98
+ resource/collection cache, and include handling.
99
+
100
+ ```js
101
+ const resource = await this.$store.dispatch('druxt/getResource', {
102
+ type: 'node--article',
103
+ id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd',
104
+ });
105
+ ```
106
+
107
+ - [The DruxtStore concept page](https://druxtjs.org/explanation/druxt-store): how it works
108
+ and why it exists.
109
+ - [DruxtStore API](https://druxtjs.org/api/packages/druxt/stores/druxt): mutations and
110
+ actions.
111
+ - [Deprecations](https://druxtjs.org/modules/druxt/deprecations): retired signatures.
112
+
113
+ ## DruxtModule
114
+
115
+ The base component for all Druxt components: it turns a `druxt()` options
116
+ object (component suggestions, props, slots) into themeable rendering.
117
+ Building on it is covered by the
118
+ [custom module tutorial](https://druxtjs.org/tutorials/first-custom-module), and the mechanism
119
+ by [Component resolution](https://druxtjs.org/explanation/component-resolution).
120
+
121
+ - [DruxtModule API](https://druxtjs.org/api/packages/druxt/components/DruxtModule).
122
+
123
+ ## Where to go next
124
+
125
+ - New to Druxt? Start with the [Getting started tutorial](https://druxtjs.org/tutorials/getting-started).
126
+ - Ready for the full site experience? See the [Site module](https://druxtjs.org/modules/site).
127
+
128
+ ---
34
129
 
35
130
  ## Modules
36
131
 
@@ -43,23 +138,36 @@ Learn more at https://druxtjs.org
43
138
  - [druxt-site](https://druxtjs.org/modules/site) - Out-of-the-box decoupled Drupal Site
44
139
  - [druxt-views](https://druxtjs.org/modules/views) - Decoupled Views
45
140
 
46
- * * *
141
+ ---
142
+
143
+ ## Links
144
+
145
+ - Documentation: https://druxtjs.org
146
+ - Community Discord server: https://discord.druxtjs.org
147
+ - Demo - Umami Food Magazine:
148
+ - https://demo.druxtjs.org
149
+ - https://storybook.umami.demo.druxtjs.org
150
+
151
+ ---
47
152
 
48
153
  ## Support
49
154
 
50
- Druxt is an open source project, built by the comunity for the community.
155
+ Druxt is an open-source project, built by the community for the community.
51
156
 
52
157
  Find support or get involved in building Druxt via the community channels:
158
+
53
159
  - [DruxtJS Discord server](https://discord.druxtjs.org)
54
160
  - **#druxt** Slack channel on [Drupal.org slack](https://drupal.org/slack)
55
161
 
56
- * * *
162
+ ---
57
163
 
58
164
  ## Contributing
59
165
 
60
- [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/druxt/druxt.js)
166
+ [![Open in DevPod!](https://devpod.sh/assets/open-in-devpod.svg)](https://devpod.sh/open#https://github.com/druxt/druxt.js)
167
+
168
+ See the [Contributing guide](https://github.com/druxt/druxt.js/blob/develop/CONTRIBUTING.md).
61
169
 
62
- * * *
170
+ ---
63
171
 
64
172
  ## License
65
173
 
package/banner.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 320" width="1280" height="320" role="img" aria-label="druxt: The core client, store and DruxtModule base."><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#1f2937"/><stop offset="0.7" stop-color="#2f495e"/><stop offset="1" stop-color="#36566e"/></linearGradient></defs><rect width="1280" height="320" rx="16" fill="url(#g)"/><rect x="250" y="85" width="150" height="150" rx="32" fill="rgba(229,236,241,0.07)"/><g transform="translate(277,112) scale(4)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.5 19.25h-19L9.4 5.5l3.4 6.45" stroke="#7cc5f1"/><path d="M12.8 11.95l2.6-5.4 6.1 12.7" stroke="#68cb9f"/></g><text x="440" y="150" style="font-family:ui-sans-serif,system-ui,sans-serif" font-size="64" font-weight="700" letter-spacing="-1" fill="#eef2f6">Druxt</text><text x="651" y="150" style="font-family:ui-monospace,Menlo,Consolas,monospace" font-size="26" fill="#7cc5f1">druxt</text><text x="440" y="196" style="font-family:ui-sans-serif,system-ui,sans-serif" font-size="25" fill="rgba(229,236,241,0.75)">The core client, store and DruxtModule base.</text><g transform="translate(440,236) scale(0.0994)"><path d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z" fill="#3498DB"/><path d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z" fill="#108775"/><path d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z" fill="#E5ECF1"/></g><text x="492" y="260" style="font-family:ui-sans-serif,system-ui,sans-serif" font-size="17" font-weight="600" fill="rgba(229,236,241,0.85)">druxtjs.org</text></svg>
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
 
3
3
  /**
4
- * Utility component for rendering Druxt modules.
4
+ * Renders the specified Druxt module component, with support for wrapper and inner elements.
5
5
  *
6
6
  * @example @lang vue
7
7
  * <Druxt
@@ -39,7 +39,7 @@ export default {
39
39
  *
40
40
  * @type {string}
41
41
  *
42
- * @example @lang vue <caption>Using the [DruxtJS Site module](https://site.druxtjs.org).</caption>
42
+ * @example @lang vue <caption>Using the [DruxtJS Site module](https://druxtjs.org/modules/site).</caption>
43
43
  * <Druxt module="site" />
44
44
  */
45
45
  module: {
@@ -52,7 +52,7 @@ export default {
52
52
  *
53
53
  * @type {object}
54
54
  *
55
- * @example @lang vue <caption>Using the [DruxtJS Entity module](https://entity.druxtjs.org) to render a 'node--article' resource.</caption>
55
+ * @example @lang vue <caption>Using the [DruxtJS Entity module](https://druxtjs.org/modules/entity) to render a 'node--article' resource.</caption>
56
56
  * <Druxt
57
57
  * module="entity"
58
58
  * :props-data="{
@@ -96,8 +96,10 @@ export default {
96
96
  /**
97
97
  * Vue.js Data object.
98
98
  *
99
- * @property {objects} components - The module and wrapper components settinsg.
100
- * @property {object} model - The model object.
99
+ * @param {object} vm - The component ViewModel.
100
+ * @param {*} vm.value - The module component model value.
101
+ * @property {object} component - The module component and its propsData.
102
+ * @property {*} model - The module component model value.
101
103
  */
102
104
  data: ({ value }) => ({
103
105
  component: {
@@ -21,8 +21,8 @@
21
21
  * The DruxtDebug component can be used to render debug information that is
22
22
  * visible only when Nuxt is run in development mode.
23
23
  *
24
- * You can render your debug information through the default slot, or via the
25
- * **json** prop to format your data, or a combination of the two.
24
+ * Use the default slot to render your debug information, or the **json**
25
+ * prop to format your data. You can also combine the two.
26
26
  *
27
27
  * @example @lang vue
28
28
  * <DruxtDebug :json="entity">Debug information</DruxtDebug>
@@ -60,6 +60,8 @@ export default {
60
60
  /**
61
61
  * The invoking Druxt module component.
62
62
  *
63
+ * @param {object} vm - The component ViewModel.
64
+ * @param {object} vm.$parent - The parent component instance.
63
65
  * @return {object}
64
66
  */
65
67
  module: ({ $parent }) =>
@@ -70,6 +72,9 @@ export default {
70
72
  /**
71
73
  * The computed summary title.
72
74
  *
75
+ * @param {object} vm - The component ViewModel.
76
+ * @param {object} vm.module - The invoking Druxt module component.
77
+ * @param {string} vm.summary - Text to use for debug summary.
73
78
  * @return {string}
74
79
  */
75
80
  title: ({ module, summary }) => module.$options._componentTag
@@ -6,28 +6,27 @@ import Vue from 'vue'
6
6
  import DruxtWrapper from './DruxtWrapper.vue'
7
7
 
8
8
  /**
9
- * The DruxtModule component is used to make a Druxt module, simply import and
10
- * extend the component to get started.
9
+ * The DruxtModule component renders a Druxt module; import and extend the
10
+ * component to build your own module.
11
11
  *
12
12
  * The component provides access to the Druxt Wrapper theming and fetch system
13
13
  * via the druxt settings object.
14
14
  *
15
+ * @see {@link https://druxtjs.org/explanation/component-resolution|Component resolution}
16
+ *
15
17
  * @example @lang js
16
- * import DruxtModule from 'druxt/dist/components/DruxtModule.vue'
18
+ * import DruxtModule from 'druxt/components/DruxtModule.vue'
17
19
  * export default {
18
- * name: 'DruxtTestModule',
20
+ * name: 'DruxtCustomModule',
19
21
  * extends: DruxtModule,
20
22
  * druxt: {
21
23
  * async fetchConfig() {},
22
24
  * async fetchData(settings) {},
23
- * componentOptions: (context) => ([[context.foo, context.bar, 'default']]),
24
- * propsData: (context) => ({
25
- * foo: context.foo,
26
- * bar: context.bar,
27
- * }),
25
+ * componentOptions: () => ([['Default']]),
26
+ * propsData: ({ model }) => ({ value: model }),
28
27
  * slots(h) {
29
28
  * return {
30
- * default: (attrs) => h('DruxtDebug', ['Hello world'])
29
+ * default: (attrs) => h('div', { attrs }, [this.model])
31
30
  * }
32
31
  * }
33
32
  * }
@@ -41,7 +40,7 @@ export default {
41
40
  /**
42
41
  * The resource langcode.
43
42
  *
44
- * @example
43
+ * @example @lang vue
45
44
  * <DruxtModule langcode="en" />
46
45
  *
47
46
  * @type {string}
@@ -52,13 +51,20 @@ export default {
52
51
  },
53
52
 
54
53
  /**
55
- * The module component model value.
54
+ * The Vue.js v-model binding for the module.
56
55
  *
57
- * Used to bypass the Drupal JSON:API fetch, setting the module data
58
- * directly.
56
+ * Setting a value provides the module data directly, so the Drupal
57
+ * JSON:API fetch is skipped.
59
58
  *
60
- * @example
61
- * <DruxtModule v-model="{ foo: bar }" />
59
+ * @example @lang vue
60
+ * <template>
61
+ * <DruxtModule v-model="model" />
62
+ * </template>
63
+ *
64
+ * <script>
65
+ * export default {
66
+ * data: () => ({ model: { foo: 'bar' } })
67
+ * }
62
68
  *
63
69
  * @type {*}
64
70
  * @model
@@ -76,9 +82,9 @@ export default {
76
82
  * @example
77
83
  * <DruxtModule
78
84
  * :wrapper="{
79
- * component: 'MyWrapper',
80
- * class: 'wrapper',
81
- * propsData: { foo: 'bar' }
85
+ * component: 'BCard',
86
+ * class: 'article-card',
87
+ * propsData: { noBody: true }
82
88
  * }"
83
89
  * />
84
90
  *
@@ -91,8 +97,10 @@ export default {
91
97
  },
92
98
 
93
99
  /**
100
+ * @param {object} vm - The component ViewModel.
101
+ * @param {*} vm.value - The module component model value.
94
102
  * @property {ComponentData} component - The wrapper component and propsData to be rendered.
95
- * @property {object} model - The model object.
103
+ * @property {*} model - The module component model value.
96
104
  */
97
105
  data: ({ value }) => ({
98
106
  component: {
@@ -110,23 +118,20 @@ export default {
110
118
  /**
111
119
  * Loads the Druxt module data and applies a wrapper component as required.
112
120
  *
113
- * **Important:** If your component has an existing `fetch` method, you must manually invoke
121
+ * If your component has an existing `fetch` method, you must manually invoke
114
122
  * the `DruxtModule.fetch()` hook.
115
123
  *
116
124
  * @example @lang js <caption>Manually invoking DruxtModule.fetch().</caption>
117
- * import DruxtModule from 'druxt/dist/components/DruxtModule.vue'
125
+ * import DruxtModule from 'druxt/components/DruxtModule.vue'
118
126
  * export default {
119
- * name: 'DruxtTestModule',
127
+ * name: 'DruxtCustomModule',
120
128
  * extends: DruxtModule,
121
129
  * async fetch() {
122
130
  * await DruxtModule.fetch.call(this)
123
- * }
131
+ * },
124
132
  * druxt: {
125
- * componentOptions: () => ([['wrapper']]),
126
- * propsData: (ctx) => ({
127
- * bar: ctx.bar,
128
- * foo: ctx.foo,
129
- * }),
133
+ * componentOptions: () => ([['Default']]),
134
+ * propsData: ({ model }) => ({ value: model }),
130
135
  * }
131
136
  * }
132
137
  */
@@ -151,7 +156,7 @@ export default {
151
156
  if (
152
157
  // No default template and wrapper isn't false OR
153
158
  (!hasDefaultTemplate && this.wrapper !== false) ||
154
- // Default tempalte and wrapper is set
159
+ // Default template and wrapper is set
155
160
  (hasDefaultTemplate && this.wrapper)
156
161
  ) {
157
162
  options = this.getModuleComponents()
@@ -190,6 +195,17 @@ export default {
190
195
  },
191
196
 
192
197
  computed: {
198
+ /**
199
+ * The current language code is the `langcode` prop if set, otherwise the
200
+ * route's langcode metadata.
201
+ *
202
+ * Used by Druxt modules to fetch the correct resource translation.
203
+ *
204
+ * @param {object} vm - The component ViewModel.
205
+ * @param {string} vm.langcode - The `langcode` prop.
206
+ * @param {object} vm.$route - The current route.
207
+ * @return {string} The current language code.
208
+ */
193
209
  lang: ({ langcode, $route }) => langcode || ($route.meta || {}).langcode
194
210
  },
195
211
 
@@ -222,6 +238,9 @@ export default {
222
238
  methods: {
223
239
  /**
224
240
  * Sets the component to render a DruxtDebug error message.
241
+ *
242
+ * @param {object} err - The error object.
243
+ * @param {object} [context] - Error context; carries the wrapper component data where the failure knows it.
225
244
  */
226
245
  error(err, context = {}) {
227
246
  // Build error details.
@@ -279,8 +298,14 @@ export default {
279
298
  // Convert parts into a pascalCase component name.
280
299
  const name = pascalCase([this.$options.name, ...parts])
281
300
 
282
- // Store set variant data to be used in next set item.
283
- variants.unshift({ global, name, parts })
301
+ // Store set variant data to be used in next set item. (Only
302
+ // `.parts` is read back from `variants` - no `global` key is
303
+ // needed here; it was previously a bare `global` object-shorthand
304
+ // property, which happened to reference Node's global object in
305
+ // SSR/webpack contexts and silently did nothing, but throws
306
+ // `ReferenceError: global is not defined` in an actual browser -
307
+ // e.g. Vite, which doesn't polyfill it.)
308
+ variants.unshift({ name, parts })
284
309
 
285
310
  // Add langcode suffixed component option.
286
311
  if (this.lang) {
@@ -315,10 +340,12 @@ export default {
315
340
  *
316
341
  * @example @lang js
317
342
  * {
318
- * bar: 'foo',
319
- * foo: 'bar',
343
+ * $attrs: { entity: {}, fields: {}, langcode: 'en', schema: {} },
344
+ * props: { value: {} },
345
+ * propsData: { entity: {}, fields: {}, langcode: 'en', schema: {}, value: {} },
320
346
  * }
321
347
  *
348
+ * @param {object} wrapperProps - Props registered by the wrapper component.
322
349
  * @return {object}
323
350
  */
324
351
  getModulePropsData(wrapperProps = {}) {
@@ -406,7 +433,7 @@ export default {
406
433
  /**
407
434
  * Get wrapper component data.
408
435
  *
409
- * @param {string} component - The Wrapper component name.
436
+ * @param {string} component - The wrapper component name.
410
437
  *
411
438
  * @return {WrapperData}
412
439
  */
@@ -443,9 +470,9 @@ export default {
443
470
  props: (this.wrapper || {}).propsData || undefined,
444
471
  }
445
472
 
446
- // Return only wrapper if fetch state is still pending and Druxt hasn't set
447
- // the available component options.
448
- if (this.$fetchState.pending && !this.component.options.length) {
473
+ // Return only wrapper if fetch state is still pending and no earlier fetch
474
+ // has resolved component options or slots.
475
+ if (this.$fetchState.pending && !(this.component.options || []).length && !(this.component.slots || []).length) {
449
476
  return h((this.wrapper || {}).component || 'div', wrapperData)
450
477
  }
451
478
 
@@ -477,6 +504,11 @@ export default {
477
504
  }
478
505
 
479
506
  /**
507
+ * The list of candidate Wrapper components, with naming parts and global
508
+ * registration state.
509
+ *
510
+ * @see {@link https://druxtjs.org/explanation/component-resolution|Component resolution}
511
+ *
480
512
  * @typedef {object[]} Components
481
513
  * @property {boolean} global - Component global registration state.
482
514
  * @property {string} name - The component name.
@@ -485,48 +517,58 @@ export default {
485
517
  * @example @lang js
486
518
  * [{
487
519
  * global: true,
488
- * pascal: 'DruxtTestModuleWrapper',
489
- * parts: ['Wrapper'],
520
+ * name: 'DruxtEntityNodeArticleDefault',
521
+ * parts: ['NodeArticle', 'Default'],
490
522
  * }]
491
523
  */
492
524
 
525
+ /* eslint-disable jsdoc/valid-types --
526
+ the property is Vue's `is`; the namepath parser rejects that word in any spelling */
493
527
  /**
528
+ * The wrapper component and propsData to be rendered.
529
+ *
494
530
  * @typedef {object} ComponentData
495
- * @property {object} $attrs - propsData not registered by the Wrapper component.
496
- * @property {string} is=DruxtWrapper - The Wrapper component name.
497
- * @property {string[]} options - The Wrapper component options.
498
- * @property {object} props - propsData registered by the Wrapper component.
531
+ * @property {object} $attrs - propsData not registered by the wrapper component.
532
+ * @property {string} is=DruxtWrapper - The wrapper component name.
533
+ * @property {string[]} options - The wrapper component options.
534
+ * @property {object} props - propsData registered by the wrapper component.
499
535
  * @property {object} propsData - The component propsData object.
500
- * @property {object} settings - Druxt settings object provided by the Wrapper component.
536
+ * @property {object} settings - Druxt settings object provided by the wrapper component.
501
537
  *
502
538
  * @example @lang js
503
539
  * {
504
- * $attrs: { bar: 'foo' },
505
- * is: 'DruxtTestModuleWrapper',
540
+ * $attrs: { entity: {}, fields: {}, schema: {} },
541
+ * is: 'DruxtEntityNodeArticleDefault',
506
542
  * options: [
507
- * 'DruxtTestModuleWrapper',
543
+ * 'DruxtEntityNodeArticleDefault',
544
+ * 'DruxtEntityDefault',
508
545
  * ],
509
- * props: { foo: 'bar' },
546
+ * props: { value: {} },
510
547
  * propsData: {
511
- * bar: 'foo',
512
- * foo: 'bar',
548
+ * entity: {},
549
+ * fields: {},
550
+ * schema: {},
551
+ * value: {},
513
552
  * },
514
- * settings: { fooBar: true },
553
+ * settings: { query: {} },
515
554
  * }
516
555
  */
556
+ /* eslint-enable jsdoc/valid-types */
517
557
 
518
558
  /**
559
+ * Druxt settings and registered props retrieved from the wrapper component.
560
+ *
519
561
  * @typedef {object} WrapperData
520
562
  * @property {object} druxt - Druxt settings object for use by Druxt module.
521
563
  * @property {object} props - Registered props oject.
522
564
  *
523
565
  * @example @lang js
524
566
  * {
525
- * druxt: { fooBar: true },
567
+ * druxt: { query: { fields: ['path', 'title'] } },
526
568
  * props: {
527
- * foo: {
528
- * type: String,
529
- * default: '',
569
+ * value: {
570
+ * type: Object,
571
+ * default: () => ({}),
530
572
  * }
531
573
  * }
532
574
  * }
@@ -7,7 +7,7 @@
7
7
  <script>
8
8
 
9
9
  /**
10
- * Placeholder component for the Druxt Wrapper themeing system.
10
+ * Placeholder component for the Druxt Wrapper theming system.
11
11
  */
12
12
  export default {
13
13
  name: 'DruxtWrapper'
package/dist/druxt.esm.js CHANGED
@@ -8,6 +8,7 @@ import merge from 'deepmerge';
8
8
  import md5 from 'md5';
9
9
  import Vue from 'vue';
10
10
 
11
+ const indexCache = new WeakMap();
11
12
  class DruxtClient {
12
13
  constructor(baseUrl, options = {}) {
13
14
  this.log = consola.create({ defaults: {
@@ -40,7 +41,13 @@ class DruxtClient {
40
41
  jsonapiResourceConfig: "jsonapi_resource_config--jsonapi_resource_config",
41
42
  ...options
42
43
  };
43
- this.index = {};
44
+ const indexKey = JSON.stringify([baseUrl, this.options.endpoint, this.options.jsonapiResourceConfig]);
45
+ this.indexKey = indexKey;
46
+ if (!indexCache.has(this.axios))
47
+ indexCache.set(this.axios, { index: {}, requests: {} });
48
+ const cache = indexCache.get(this.axios);
49
+ this.indexRequests = cache.requests;
50
+ this.index = cache.index[indexKey] || (cache.index[indexKey] = {});
44
51
  }
45
52
  addHeaders(headers) {
46
53
  if (typeof headers === "undefined") {
@@ -161,12 +168,19 @@ class DruxtClient {
161
168
  return collections;
162
169
  }
163
170
  async getIndex(resource, prefix) {
164
- if ((this.index || {})[prefix] && !resource) {
165
- return this.index[prefix];
166
- }
167
- if ((this.index || {})[prefix] && resource) {
168
- return this.index[prefix][resource] ? this.index[prefix][resource] : false;
171
+ if (!(this.index || {})[prefix]) {
172
+ const key = [this.indexKey, prefix || ""].join(":");
173
+ const request = this.indexRequests[key] || (this.indexRequests[key] = this.fetchIndex(prefix));
174
+ try {
175
+ await request;
176
+ } finally {
177
+ if (this.indexRequests[key] === request)
178
+ delete this.indexRequests[key];
179
+ }
169
180
  }
181
+ return resource ? this.index[prefix][resource] || false : this.index[prefix];
182
+ }
183
+ async fetchIndex(prefix) {
170
184
  const url = [prefix, this.options.endpoint].join("");
171
185
  const { data } = await this.get(url);
172
186
  let index = data.links;
@@ -187,13 +201,13 @@ class DruxtClient {
187
201
  this.log.warn(err.message);
188
202
  }
189
203
  for (const resourceType in resources) {
190
- const resource2 = resources[resourceType];
191
- const internal = resource2.attributes.drupal_internal__id.split("--");
204
+ const resource = resources[resourceType];
205
+ const internal = resource.attributes.drupal_internal__id.split("--");
192
206
  const item = {
193
- resourceType: resource2.attributes.resourceType,
207
+ resourceType: resource.attributes.resourceType,
194
208
  entityType: internal[0],
195
209
  bundle: internal[1],
196
- resourceFields: resource2.attributes.resourceFields
210
+ resourceFields: resource.attributes.resourceFields
197
211
  };
198
212
  const id = [item.entityType, item.bundle].join("--");
199
213
  index[id] = {
@@ -203,7 +217,6 @@ class DruxtClient {
203
217
  }
204
218
  }
205
219
  this.index[prefix] = index;
206
- return resource ? this.index[prefix][resource] || false : this.index[prefix];
207
220
  }
208
221
  async getRelated(type, id, related, query, prefix = "") {
209
222
  if (!id || !type || !related) {
@@ -250,7 +263,7 @@ class DruxtClient {
250
263
  }
251
264
 
252
265
  var name = "druxt";
253
- var version = "0.24.0";
266
+ var version = "0.24.1-dev.20260920063819";
254
267
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
255
268
  var keywords = [
256
269
  "cms",
@@ -273,12 +286,14 @@ var repository = {
273
286
  url: "git+https://github.com/druxt/druxt.js"
274
287
  };
275
288
  var license = "MIT";
289
+ var author = "Stuart Clark <stuart@druxtjs.org>";
276
290
  var exports = {
277
291
  ".": {
278
292
  require: "./dist/druxt.ssr.js",
279
293
  "import": "./dist/druxt.esm.js"
280
294
  },
281
295
  "./components/*": "./dist/components/*",
296
+ "./dist/components/*": "./dist/components/*",
282
297
  "./plugins/*": "./dist/plugins/*",
283
298
  "./server-middleware/*": "./dist/server-middleware/*",
284
299
  "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
@@ -287,31 +302,34 @@ var main = "dist/druxt.ssr.js";
287
302
  var module = "dist/druxt.esm.js";
288
303
  var files = [
289
304
  "dist",
290
- "templates"
305
+ "templates",
306
+ "icon.svg",
307
+ "banner.svg"
291
308
  ];
292
309
  var dependencies = {
293
310
  "@nuxtjs/axios": "^5.13.6",
294
311
  "@nuxtjs/proxy": "^2.1.0",
295
- "@vue/devtools-api": "^6.5.0",
312
+ "@vue/devtools-api": "^6.6.4",
296
313
  chalk: "^4.1.2",
297
314
  deepmerge: "^4.3.1",
298
- "drupal-jsonapi-params": "^2.2.0",
299
- "express-rate-limit": "^6.7.0",
300
- "launch-editor": "^2.6.0",
315
+ "drupal-jsonapi-params": "^2.3.2",
316
+ "express-rate-limit": "^7.5.1",
317
+ "launch-editor": "^2.14.1",
301
318
  md5: "^2.3.0",
302
319
  querystring: "^0.2.1",
303
320
  scule: "^0.3.2"
304
321
  };
305
322
  var devDependencies = {
306
- "@storybook/addon-docs": "^6.5.16"
323
+ "@storybook/addon-docs": "^6.5.16",
324
+ "druxt-test-utils": "^0.2.15"
307
325
  };
308
326
  var peerDependencies = {
309
- axios: "*",
327
+ axios: "0.28.0",
310
328
  consola: "*"
311
329
  };
312
330
  var optionalDependencies = {
313
- "core-js": "^3.31.0",
314
- vue: "^2.7.14",
331
+ "core-js": "^3.50.0",
332
+ vue: "^2.7.16",
315
333
  vuex: "^3.6.2"
316
334
  };
317
335
  var publishConfig = {
@@ -326,6 +344,7 @@ var meta = {
326
344
  bugs: bugs,
327
345
  repository: repository,
328
346
  license: license,
347
+ author: author,
329
348
  exports: exports,
330
349
  main: main,
331
350
  module: module,
@@ -337,6 +356,7 @@ var meta = {
337
356
  publishConfig: publishConfig
338
357
  };
339
358
 
359
+ const extendComponent = (component) => ({ ...component, isAsync: false });
340
360
  const DruxtNuxtModule = async function(moduleOptions = {}) {
341
361
  var _a;
342
362
  const options = {
@@ -393,8 +413,13 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
393
413
  };
394
414
  this.addModule("@nuxtjs/axios");
395
415
  }
416
+ this.options.build = this.options.build || {};
417
+ this.options.build.transpile = this.options.build.transpile || [];
418
+ if (!this.options.build.transpile.includes("axios")) {
419
+ this.options.build.transpile.push("axios");
420
+ }
396
421
  this.nuxt.hook("components:dirs", (dirs) => {
397
- dirs.push({ path: join(__dirname, "components") });
422
+ dirs.push({ path: join(__dirname, "components"), extendComponent });
398
423
  });
399
424
  this.addPlugin({
400
425
  src: resolve(__dirname, "../templates/plugin.js"),
@@ -493,14 +518,16 @@ const DruxtStore = ({ store }) => {
493
518
  collection.data = dehydrateResources({ commit: this.commit, prefix, queryObject, resources: collection.data });
494
519
  if (collection.included) {
495
520
  collection.included = dehydrateResources({ commit: this.commit, prefix, queryObject, resources: collection.included });
496
- delete collection.included;
497
521
  }
522
+ const hadIncluded = !!collection.included;
498
523
  collection = merge(state.collections[type][hash][prefix] || {}, collection, { arrayMerge: (dst, src) => src });
524
+ if (!hadIncluded)
525
+ delete collection.included;
499
526
  Vue.set(state.collections[type][hash], prefix, collection);
500
527
  },
501
528
  addResource(state, { prefix, resource, hash }) {
502
529
  if (hash) {
503
- console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash");
530
+ console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/modules/druxt/deprecations#druxtstore-addresource-hash");
504
531
  }
505
532
  const { id, type } = (resource || {}).data || {};
506
533
  if (!id || !type) {
@@ -548,10 +575,17 @@ const DruxtStore = ({ store }) => {
548
575
  const queryObject = getDrupalJsonApiParams(query).getQueryObject();
549
576
  const hash = query ? md5(JSON.stringify({ ...queryObject, fields: {}, include: [] })) : "_default";
550
577
  if (!bypassCache && ((state.collections[type] || {})[hash] || {})[prefix]) {
551
- return {
552
- ...state.collections[type][hash][prefix],
553
- data: state.collections[type][hash][prefix].data.map((o) => ((state.resources[o.type][o.id] || {})[prefix] || {}).data)
554
- };
578
+ const cached = state.collections[type][hash][prefix];
579
+ const hydrate = (o) => (((state.resources[o.type] || {})[o.id] || {})[prefix] || {}).data;
580
+ const data = cached.data.map(hydrate);
581
+ const included = cached.included ? cached.included.map(hydrate) : void 0;
582
+ if (data.every((o) => o) && (included || []).every((o) => o)) {
583
+ return {
584
+ ...cached,
585
+ data,
586
+ ...included ? { included } : {}
587
+ };
588
+ }
555
589
  }
556
590
  const collection = await this.$druxt.getCollection(type, query, prefix);
557
591
  commit("addCollection", { collection: { ...collection }, type, hash, prefix });
package/dist/druxt.ssr.js CHANGED
@@ -21,6 +21,7 @@ const merge__default = /*#__PURE__*/_interopDefaultLegacy(merge);
21
21
  const md5__default = /*#__PURE__*/_interopDefaultLegacy(md5);
22
22
  const Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue);
23
23
 
24
+ const indexCache = new WeakMap();
24
25
  class DruxtClient {
25
26
  constructor(baseUrl, options = {}) {
26
27
  this.log = consola__default["default"].create({ defaults: {
@@ -53,7 +54,13 @@ class DruxtClient {
53
54
  jsonapiResourceConfig: "jsonapi_resource_config--jsonapi_resource_config",
54
55
  ...options
55
56
  };
56
- this.index = {};
57
+ const indexKey = JSON.stringify([baseUrl, this.options.endpoint, this.options.jsonapiResourceConfig]);
58
+ this.indexKey = indexKey;
59
+ if (!indexCache.has(this.axios))
60
+ indexCache.set(this.axios, { index: {}, requests: {} });
61
+ const cache = indexCache.get(this.axios);
62
+ this.indexRequests = cache.requests;
63
+ this.index = cache.index[indexKey] || (cache.index[indexKey] = {});
57
64
  }
58
65
  addHeaders(headers) {
59
66
  if (typeof headers === "undefined") {
@@ -174,12 +181,19 @@ class DruxtClient {
174
181
  return collections;
175
182
  }
176
183
  async getIndex(resource, prefix) {
177
- if ((this.index || {})[prefix] && !resource) {
178
- return this.index[prefix];
179
- }
180
- if ((this.index || {})[prefix] && resource) {
181
- return this.index[prefix][resource] ? this.index[prefix][resource] : false;
184
+ if (!(this.index || {})[prefix]) {
185
+ const key = [this.indexKey, prefix || ""].join(":");
186
+ const request = this.indexRequests[key] || (this.indexRequests[key] = this.fetchIndex(prefix));
187
+ try {
188
+ await request;
189
+ } finally {
190
+ if (this.indexRequests[key] === request)
191
+ delete this.indexRequests[key];
192
+ }
182
193
  }
194
+ return resource ? this.index[prefix][resource] || false : this.index[prefix];
195
+ }
196
+ async fetchIndex(prefix) {
183
197
  const url = [prefix, this.options.endpoint].join("");
184
198
  const { data } = await this.get(url);
185
199
  let index = data.links;
@@ -200,13 +214,13 @@ class DruxtClient {
200
214
  this.log.warn(err.message);
201
215
  }
202
216
  for (const resourceType in resources) {
203
- const resource2 = resources[resourceType];
204
- const internal = resource2.attributes.drupal_internal__id.split("--");
217
+ const resource = resources[resourceType];
218
+ const internal = resource.attributes.drupal_internal__id.split("--");
205
219
  const item = {
206
- resourceType: resource2.attributes.resourceType,
220
+ resourceType: resource.attributes.resourceType,
207
221
  entityType: internal[0],
208
222
  bundle: internal[1],
209
- resourceFields: resource2.attributes.resourceFields
223
+ resourceFields: resource.attributes.resourceFields
210
224
  };
211
225
  const id = [item.entityType, item.bundle].join("--");
212
226
  index[id] = {
@@ -216,7 +230,6 @@ class DruxtClient {
216
230
  }
217
231
  }
218
232
  this.index[prefix] = index;
219
- return resource ? this.index[prefix][resource] || false : this.index[prefix];
220
233
  }
221
234
  async getRelated(type, id, related, query, prefix = "") {
222
235
  if (!id || !type || !related) {
@@ -263,7 +276,7 @@ class DruxtClient {
263
276
  }
264
277
 
265
278
  var name = "druxt";
266
- var version = "0.24.0";
279
+ var version = "0.24.1-dev.20260920063819";
267
280
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
268
281
  var keywords = [
269
282
  "cms",
@@ -286,12 +299,14 @@ var repository = {
286
299
  url: "git+https://github.com/druxt/druxt.js"
287
300
  };
288
301
  var license = "MIT";
302
+ var author = "Stuart Clark <stuart@druxtjs.org>";
289
303
  var exports$1 = {
290
304
  ".": {
291
305
  require: "./dist/druxt.ssr.js",
292
306
  "import": "./dist/druxt.esm.js"
293
307
  },
294
308
  "./components/*": "./dist/components/*",
309
+ "./dist/components/*": "./dist/components/*",
295
310
  "./plugins/*": "./dist/plugins/*",
296
311
  "./server-middleware/*": "./dist/server-middleware/*",
297
312
  "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
@@ -300,31 +315,34 @@ var main = "dist/druxt.ssr.js";
300
315
  var module$1 = "dist/druxt.esm.js";
301
316
  var files = [
302
317
  "dist",
303
- "templates"
318
+ "templates",
319
+ "icon.svg",
320
+ "banner.svg"
304
321
  ];
305
322
  var dependencies = {
306
323
  "@nuxtjs/axios": "^5.13.6",
307
324
  "@nuxtjs/proxy": "^2.1.0",
308
- "@vue/devtools-api": "^6.5.0",
325
+ "@vue/devtools-api": "^6.6.4",
309
326
  chalk: "^4.1.2",
310
327
  deepmerge: "^4.3.1",
311
- "drupal-jsonapi-params": "^2.2.0",
312
- "express-rate-limit": "^6.7.0",
313
- "launch-editor": "^2.6.0",
328
+ "drupal-jsonapi-params": "^2.3.2",
329
+ "express-rate-limit": "^7.5.1",
330
+ "launch-editor": "^2.14.1",
314
331
  md5: "^2.3.0",
315
332
  querystring: "^0.2.1",
316
333
  scule: "^0.3.2"
317
334
  };
318
335
  var devDependencies = {
319
- "@storybook/addon-docs": "^6.5.16"
336
+ "@storybook/addon-docs": "^6.5.16",
337
+ "druxt-test-utils": "^0.2.15"
320
338
  };
321
339
  var peerDependencies = {
322
- axios: "*",
340
+ axios: "0.28.0",
323
341
  consola: "*"
324
342
  };
325
343
  var optionalDependencies = {
326
- "core-js": "^3.31.0",
327
- vue: "^2.7.14",
344
+ "core-js": "^3.50.0",
345
+ vue: "^2.7.16",
328
346
  vuex: "^3.6.2"
329
347
  };
330
348
  var publishConfig = {
@@ -339,6 +357,7 @@ const meta = {
339
357
  bugs: bugs,
340
358
  repository: repository,
341
359
  license: license,
360
+ author: author,
342
361
  exports: exports$1,
343
362
  main: main,
344
363
  module: module$1,
@@ -350,6 +369,7 @@ const meta = {
350
369
  publishConfig: publishConfig
351
370
  };
352
371
 
372
+ const extendComponent = (component) => ({ ...component, isAsync: false });
353
373
  const DruxtNuxtModule = async function(moduleOptions = {}) {
354
374
  var _a;
355
375
  const options = {
@@ -406,8 +426,13 @@ const DruxtNuxtModule = async function(moduleOptions = {}) {
406
426
  };
407
427
  this.addModule("@nuxtjs/axios");
408
428
  }
429
+ this.options.build = this.options.build || {};
430
+ this.options.build.transpile = this.options.build.transpile || [];
431
+ if (!this.options.build.transpile.includes("axios")) {
432
+ this.options.build.transpile.push("axios");
433
+ }
409
434
  this.nuxt.hook("components:dirs", (dirs) => {
410
- dirs.push({ path: path.join(__dirname, "components") });
435
+ dirs.push({ path: path.join(__dirname, "components"), extendComponent });
411
436
  });
412
437
  this.addPlugin({
413
438
  src: path.resolve(__dirname, "../templates/plugin.js"),
@@ -506,14 +531,16 @@ const DruxtStore = ({ store }) => {
506
531
  collection.data = dehydrateResources({ commit: this.commit, prefix, queryObject, resources: collection.data });
507
532
  if (collection.included) {
508
533
  collection.included = dehydrateResources({ commit: this.commit, prefix, queryObject, resources: collection.included });
509
- delete collection.included;
510
534
  }
535
+ const hadIncluded = !!collection.included;
511
536
  collection = merge__default["default"](state.collections[type][hash][prefix] || {}, collection, { arrayMerge: (dst, src) => src });
537
+ if (!hadIncluded)
538
+ delete collection.included;
512
539
  Vue__default["default"].set(state.collections[type][hash], prefix, collection);
513
540
  },
514
541
  addResource(state, { prefix, resource, hash }) {
515
542
  if (hash) {
516
- console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash");
543
+ console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/modules/druxt/deprecations#druxtstore-addresource-hash");
517
544
  }
518
545
  const { id, type } = (resource || {}).data || {};
519
546
  if (!id || !type) {
@@ -561,10 +588,17 @@ const DruxtStore = ({ store }) => {
561
588
  const queryObject = getDrupalJsonApiParams(query).getQueryObject();
562
589
  const hash = query ? md5__default["default"](JSON.stringify({ ...queryObject, fields: {}, include: [] })) : "_default";
563
590
  if (!bypassCache && ((state.collections[type] || {})[hash] || {})[prefix]) {
564
- return {
565
- ...state.collections[type][hash][prefix],
566
- data: state.collections[type][hash][prefix].data.map((o) => ((state.resources[o.type][o.id] || {})[prefix] || {}).data)
567
- };
591
+ const cached = state.collections[type][hash][prefix];
592
+ const hydrate = (o) => (((state.resources[o.type] || {})[o.id] || {})[prefix] || {}).data;
593
+ const data = cached.data.map(hydrate);
594
+ const included = cached.included ? cached.included.map(hydrate) : void 0;
595
+ if (data.every((o) => o) && (included || []).every((o) => o)) {
596
+ return {
597
+ ...cached,
598
+ data,
599
+ ...included ? { included } : {}
600
+ };
601
+ }
568
602
  }
569
603
  const collection = await this.$druxt.getCollection(type, query, prefix);
570
604
  commit("addCollection", { collection: { ...collection }, type, hash, prefix });
@@ -63,11 +63,11 @@ app.post("/add", async (req, res) => {
63
63
  }
64
64
  }
65
65
  }
66
- const formatRecrusive = (item, depth = 2) => {
66
+ const formatRecursive = (item, depth = 2) => {
67
67
  for (const [key, value] of Object.entries(item)) {
68
68
  if (typeof value === "object") {
69
69
  exports.push(`${" ".repeat(depth * 2)}${key}: {`);
70
- formatRecrusive(value, depth + 1);
70
+ formatRecursive(value, depth + 1);
71
71
  exports.push(`${" ".repeat(depth * 2)}},`);
72
72
  } else if (typeof value === "string") {
73
73
  exports.push(`${" ".repeat(depth * 2)}${value},`);
@@ -75,7 +75,7 @@ app.post("/add", async (req, res) => {
75
75
  }
76
76
  };
77
77
  exports.push("\r\n druxt: {");
78
- formatRecrusive(druxt);
78
+ formatRecursive(druxt);
79
79
  exports.push(" },");
80
80
  }
81
81
  const template = `<!-- Generated by the Druxt devel template server middleware. -->
package/icon.svg ADDED
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path stroke="#036397" d="M21.5 19.25h-19L9.4 5.5l3.4 6.45" />
3
+ <path stroke="#3b8070" d="M12.8 11.95l2.6-5.4 6.1 12.7" />
4
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "druxt",
3
- "version": "0.24.0",
3
+ "version": "0.24.1-dev.20260920063819",
4
4
  "description": "The Fully Decoupled Drupal Framework for Nuxt.js.",
5
5
  "keywords": [
6
6
  "cms",
@@ -23,12 +23,14 @@
23
23
  "url": "git+https://github.com/druxt/druxt.js"
24
24
  },
25
25
  "license": "MIT",
26
+ "author": "Stuart Clark <stuart@druxtjs.org>",
26
27
  "exports": {
27
28
  ".": {
28
29
  "require": "./dist/druxt.ssr.js",
29
30
  "import": "./dist/druxt.esm.js"
30
31
  },
31
32
  "./components/*": "./dist/components/*",
33
+ "./dist/components/*": "./dist/components/*",
32
34
  "./plugins/*": "./dist/plugins/*",
33
35
  "./server-middleware/*": "./dist/server-middleware/*",
34
36
  "./dist/server-middleware/template.mjs": "./dist/server-middleware/template.mjs"
@@ -37,31 +39,34 @@
37
39
  "module": "dist/druxt.esm.js",
38
40
  "files": [
39
41
  "dist",
40
- "templates"
42
+ "templates",
43
+ "icon.svg",
44
+ "banner.svg"
41
45
  ],
42
46
  "dependencies": {
43
47
  "@nuxtjs/axios": "^5.13.6",
44
48
  "@nuxtjs/proxy": "^2.1.0",
45
- "@vue/devtools-api": "^6.5.0",
49
+ "@vue/devtools-api": "^6.6.4",
46
50
  "chalk": "^4.1.2",
47
51
  "deepmerge": "^4.3.1",
48
- "drupal-jsonapi-params": "^2.2.0",
49
- "express-rate-limit": "^6.7.0",
50
- "launch-editor": "^2.6.0",
52
+ "drupal-jsonapi-params": "^2.3.2",
53
+ "express-rate-limit": "^7.5.1",
54
+ "launch-editor": "^2.14.1",
51
55
  "md5": "^2.3.0",
52
56
  "querystring": "^0.2.1",
53
57
  "scule": "^0.3.2"
54
58
  },
55
59
  "devDependencies": {
56
- "@storybook/addon-docs": "^6.5.16"
60
+ "@storybook/addon-docs": "^6.5.16",
61
+ "druxt-test-utils": "^0.2.15"
57
62
  },
58
63
  "peerDependencies": {
59
- "axios": "*",
64
+ "axios": "0.28.0",
60
65
  "consola": "*"
61
66
  },
62
67
  "optionalDependencies": {
63
- "core-js": "^3.31.0",
64
- "vue": "^2.7.14",
68
+ "core-js": "^3.50.0",
69
+ "vue": "^2.7.16",
65
70
  "vuex": "^3.6.2"
66
71
  },
67
72
  "publishConfig": {