nextjs-cms 0.9.22 → 0.9.24

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.
Files changed (192) hide show
  1. package/README.md +65 -13
  2. package/dist/api/actions/files.d.ts +30 -0
  3. package/dist/api/actions/files.d.ts.map +1 -0
  4. package/dist/api/actions/files.js +234 -0
  5. package/dist/api/actions/index.d.ts +4 -0
  6. package/dist/api/actions/index.d.ts.map +1 -0
  7. package/dist/api/actions/index.js +3 -0
  8. package/dist/api/actions/pages.d.ts +297 -0
  9. package/dist/api/actions/pages.d.ts.map +1 -0
  10. package/dist/api/actions/pages.js +1215 -0
  11. package/dist/api/actions/privileges.d.ts +25 -0
  12. package/dist/api/actions/privileges.d.ts.map +1 -0
  13. package/dist/api/actions/privileges.js +98 -0
  14. package/dist/api/client/index.d.ts +4 -0
  15. package/dist/api/client/index.d.ts.map +1 -0
  16. package/dist/api/client/index.js +3 -0
  17. package/dist/api/client.d.ts +30 -0
  18. package/dist/api/client.d.ts.map +1 -0
  19. package/dist/api/client.js +82 -0
  20. package/dist/api/index.d.ts +1 -938
  21. package/dist/api/index.d.ts.map +1 -1
  22. package/dist/api/index.js +0 -13
  23. package/dist/api/plugin/index.d.ts +7 -0
  24. package/dist/api/plugin/index.d.ts.map +1 -0
  25. package/dist/api/plugin/index.js +5 -0
  26. package/dist/api/root.d.ts +18 -1844
  27. package/dist/api/root.d.ts.map +1 -1
  28. package/dist/api/root.js +18 -83
  29. package/dist/api/server/index.d.ts +8 -0
  30. package/dist/api/server/index.d.ts.map +1 -0
  31. package/dist/api/server/index.js +3 -0
  32. package/dist/api/server.d.ts +2748 -0
  33. package/dist/api/server.d.ts.map +1 -0
  34. package/dist/api/server.js +100 -0
  35. package/dist/api/trpc/client.d.ts +19 -3
  36. package/dist/api/trpc/client.d.ts.map +1 -1
  37. package/dist/api/trpc/client.js +55 -1
  38. package/dist/api/trpc/query-client.d.ts +3 -1
  39. package/dist/api/trpc/query-client.d.ts.map +1 -1
  40. package/dist/api/trpc/query-client.js +25 -20
  41. package/dist/api/trpc/root.d.ts +906 -0
  42. package/dist/api/trpc/root.d.ts.map +1 -0
  43. package/dist/api/trpc/root.js +47 -0
  44. package/dist/api/trpc/routers/accountSettings.d.ts +66 -0
  45. package/dist/api/trpc/routers/accountSettings.d.ts.map +1 -0
  46. package/dist/api/trpc/routers/accountSettings.js +200 -0
  47. package/dist/api/trpc/routers/admins.d.ts +112 -0
  48. package/dist/api/trpc/routers/admins.d.ts.map +1 -0
  49. package/dist/api/trpc/routers/admins.js +331 -0
  50. package/dist/api/trpc/routers/auth.d.ts +54 -0
  51. package/dist/api/trpc/routers/auth.d.ts.map +1 -0
  52. package/dist/api/trpc/routers/auth.js +50 -0
  53. package/dist/api/trpc/routers/categorySection.d.ts +105 -0
  54. package/dist/api/trpc/routers/categorySection.d.ts.map +1 -0
  55. package/dist/api/trpc/routers/categorySection.js +49 -0
  56. package/dist/api/trpc/routers/config.d.ts +48 -0
  57. package/dist/api/trpc/routers/config.d.ts.map +1 -0
  58. package/dist/api/trpc/routers/config.js +18 -0
  59. package/dist/api/trpc/routers/cpanel.d.ts +82 -0
  60. package/dist/api/trpc/routers/cpanel.d.ts.map +1 -0
  61. package/dist/api/trpc/routers/cpanel.js +216 -0
  62. package/dist/api/trpc/routers/fields.d.ts +35 -0
  63. package/dist/api/trpc/routers/fields.d.ts.map +1 -0
  64. package/dist/api/trpc/routers/fields.js +81 -0
  65. package/dist/api/trpc/routers/files.d.ts +34 -0
  66. package/dist/api/trpc/routers/files.d.ts.map +1 -0
  67. package/dist/api/trpc/routers/files.js +14 -0
  68. package/dist/api/trpc/routers/gallery.d.ts +35 -0
  69. package/dist/api/trpc/routers/gallery.d.ts.map +1 -0
  70. package/dist/api/trpc/routers/gallery.js +92 -0
  71. package/dist/api/trpc/routers/hasItemsSection.d.ts +194 -0
  72. package/dist/api/trpc/routers/hasItemsSection.d.ts.map +1 -0
  73. package/dist/api/trpc/routers/hasItemsSection.js +86 -0
  74. package/dist/api/trpc/routers/logs.d.ts +59 -0
  75. package/dist/api/trpc/routers/logs.d.ts.map +1 -0
  76. package/dist/api/trpc/routers/logs.js +79 -0
  77. package/dist/api/trpc/routers/navigation.d.ts +65 -0
  78. package/dist/api/trpc/routers/navigation.d.ts.map +1 -0
  79. package/dist/api/trpc/routers/navigation.js +11 -0
  80. package/dist/api/trpc/routers/simpleSection.d.ts +93 -0
  81. package/dist/api/trpc/routers/simpleSection.d.ts.map +1 -0
  82. package/dist/api/trpc/routers/simpleSection.js +54 -0
  83. package/dist/api/trpc/server.d.ts +2789 -5
  84. package/dist/api/trpc/server.d.ts.map +1 -1
  85. package/dist/api/trpc/server.js +91 -52
  86. package/dist/api/trpc/trpc.d.ts +111 -0
  87. package/dist/api/trpc/trpc.d.ts.map +1 -0
  88. package/dist/api/trpc/trpc.js +99 -0
  89. package/dist/api/trpc/utils/async-caller-proxy.d.ts +2 -0
  90. package/dist/api/trpc/utils/async-caller-proxy.d.ts.map +1 -0
  91. package/dist/api/trpc/utils/async-caller-proxy.js +38 -0
  92. package/dist/api/trpc/utils/refresh-token-link.d.ts +6 -0
  93. package/dist/api/trpc/utils/refresh-token-link.d.ts.map +1 -0
  94. package/dist/api/trpc/utils/refresh-token-link.js +81 -0
  95. package/dist/api/trpc/utils/router-types.d.ts +7 -0
  96. package/dist/api/trpc/utils/router-types.d.ts.map +1 -0
  97. package/dist/api/trpc/utils/router-types.js +0 -0
  98. package/dist/api/use-axios-private.d.ts +6 -0
  99. package/dist/api/use-axios-private.d.ts.map +1 -0
  100. package/dist/api/use-axios-private.js +57 -0
  101. package/dist/api/utils/async-caller-proxy.d.ts +2 -0
  102. package/dist/api/utils/async-caller-proxy.d.ts.map +1 -0
  103. package/dist/api/utils/async-caller-proxy.js +36 -0
  104. package/dist/api/utils/lazy-caller-proxy.d.ts +2 -0
  105. package/dist/api/utils/lazy-caller-proxy.d.ts.map +1 -0
  106. package/dist/api/utils/lazy-caller-proxy.js +36 -0
  107. package/dist/api/utils/router-types.d.ts +7 -0
  108. package/dist/api/utils/router-types.d.ts.map +1 -0
  109. package/dist/api/utils/router-types.js +0 -0
  110. package/dist/auth/hooks/index.d.ts +1 -2
  111. package/dist/auth/hooks/index.d.ts.map +1 -1
  112. package/dist/auth/hooks/index.js +1 -2
  113. package/dist/auth/react.d.ts +1 -2
  114. package/dist/auth/react.d.ts.map +1 -1
  115. package/dist/auth/react.js +1 -2
  116. package/dist/auth/trpc.d.ts +1 -1
  117. package/dist/auth/trpc.d.ts.map +1 -1
  118. package/dist/auth/trpc.js +0 -1
  119. package/dist/cli/lib/fix-master-admin.d.ts.map +1 -1
  120. package/dist/cli/lib/fix-master-admin.js +12 -25
  121. package/dist/cli/lib/update-sections.d.ts.map +1 -1
  122. package/dist/cli/lib/update-sections.js +29 -24
  123. package/dist/core/config/config-loader.d.ts +40 -16
  124. package/dist/core/config/config-loader.d.ts.map +1 -1
  125. package/dist/core/config/config-loader.js +58 -10
  126. package/dist/core/config/index.d.ts +1 -1
  127. package/dist/core/config/index.d.ts.map +1 -1
  128. package/dist/core/config/loader-with-jiti.d.ts.map +1 -1
  129. package/dist/core/config/loader-with-jiti.js +13 -12
  130. package/dist/core/factories/section-factory-with-jiti.d.ts.map +1 -1
  131. package/dist/core/factories/section-factory-with-jiti.js +2 -1
  132. package/dist/core/sections/category.d.ts +6 -6
  133. package/dist/core/sections/hasItems.d.ts +6 -6
  134. package/dist/core/sections/section.d.ts +4 -4
  135. package/dist/core/sections/simple.d.ts +2 -2
  136. package/dist/core/types/index.d.ts +17 -0
  137. package/dist/core/types/index.d.ts.map +1 -1
  138. package/dist/index.d.ts +0 -1
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +0 -1
  141. package/dist/plugins/client.d.ts +19 -0
  142. package/dist/plugins/client.d.ts.map +1 -0
  143. package/dist/plugins/client.js +24 -0
  144. package/dist/plugins/define.d.ts +6 -0
  145. package/dist/plugins/define.d.ts.map +1 -0
  146. package/dist/plugins/define.js +3 -0
  147. package/dist/plugins/derive.d.ts +32 -0
  148. package/dist/plugins/derive.d.ts.map +1 -0
  149. package/dist/plugins/derive.js +77 -0
  150. package/dist/plugins/index.d.ts +1 -1
  151. package/dist/plugins/index.d.ts.map +1 -1
  152. package/dist/plugins/loader.d.ts +43 -51
  153. package/dist/plugins/loader.d.ts.map +1 -1
  154. package/dist/plugins/loader.js +115 -58
  155. package/dist/plugins/manifest.d.ts +28 -0
  156. package/dist/plugins/manifest.d.ts.map +1 -0
  157. package/dist/plugins/manifest.js +83 -0
  158. package/dist/plugins/prefetch.d.ts +16 -0
  159. package/dist/plugins/prefetch.d.ts.map +1 -0
  160. package/dist/plugins/prefetch.js +40 -0
  161. package/dist/plugins/registry.d.ts +22 -0
  162. package/dist/plugins/registry.d.ts.map +1 -0
  163. package/dist/plugins/registry.js +25 -0
  164. package/dist/plugins/server.d.ts +2 -0
  165. package/dist/plugins/server.d.ts.map +1 -1
  166. package/dist/plugins/server.js +2 -0
  167. package/dist/plugins/types.d.ts +79 -0
  168. package/dist/plugins/types.d.ts.map +1 -0
  169. package/dist/plugins/types.js +0 -0
  170. package/dist/translations/base/en.d.ts +1 -0
  171. package/dist/translations/base/en.d.ts.map +1 -1
  172. package/dist/translations/base/en.js +1 -0
  173. package/dist/translations/client.d.ts +16 -4
  174. package/dist/translations/client.d.ts.map +1 -1
  175. package/dist/translations/server.d.ts +16 -4
  176. package/dist/translations/server.d.ts.map +1 -1
  177. package/dist/utils/console-log.d.ts +18 -0
  178. package/dist/utils/console-log.d.ts.map +1 -0
  179. package/dist/utils/console-log.js +28 -0
  180. package/dist/utils/index.d.ts +1 -0
  181. package/dist/utils/index.d.ts.map +1 -1
  182. package/dist/utils/index.js +1 -0
  183. package/dist/utils/log.d.ts +18 -0
  184. package/dist/utils/log.d.ts.map +1 -0
  185. package/dist/utils/log.js +28 -0
  186. package/dist/validators/index.d.ts +1 -0
  187. package/dist/validators/index.d.ts.map +1 -1
  188. package/dist/validators/index.js +1 -0
  189. package/dist/validators/tags.d.ts +4 -0
  190. package/dist/validators/tags.d.ts.map +1 -0
  191. package/dist/validators/tags.js +8 -0
  192. package/package.json +28 -18
@@ -5,30 +5,21 @@ if (typeof window !== 'undefined') {
5
5
  import { createRequire } from 'module';
6
6
  import path from 'path';
7
7
  import { getCMSConfig, getConfigImportVersion } from '../core/config/index.js';
8
- import chalk from 'chalk';
9
- export function definePlugin(plugin) {
10
- return plugin;
11
- }
12
- const deriveRouterKey = (packageName) => {
13
- const baseName = packageName.split('/').pop() ?? packageName;
14
- const withoutPrefix = baseName.startsWith('plugin-') ? baseName.slice('plugin-'.length) : baseName;
15
- const parts = withoutPrefix.split(/[-_]/g).filter(Boolean);
16
- const [first, ...rest] = parts;
17
- let key = first ? first + rest.map((part) => part[0]?.toUpperCase() + part.slice(1)).join('') : withoutPrefix;
18
- if (!key) {
19
- key = baseName.replace(/[^A-Za-z0-9_]/g, '_');
20
- }
21
- if (!/^[A-Za-z_]/.test(key)) {
22
- key = `plugin${key ? key[0]?.toUpperCase() + key.slice(1) : ''}`;
23
- }
24
- return key;
25
- };
8
+ import { derivePluginName, derivePluginRouterKey } from './derive.js';
9
+ import { logWarn, logError, logInfo } from '../utils/console-log.js';
26
10
  let cachedPlugins = null;
27
11
  let cachedConfigVersion = -1;
28
12
  let loadPromise = null;
29
13
  let jitiInstance = null;
30
14
  const appRequire = createRequire(path.join(process.cwd(), 'package.json'));
31
15
  const pkgRequire = createRequire(import.meta.url);
16
+ let serverOnlyEmptyPath = null;
17
+ const getServerOnlyEmptyPath = () => {
18
+ if (!serverOnlyEmptyPath) {
19
+ serverOnlyEmptyPath = path.join(path.dirname(pkgRequire.resolve('server-only')), 'empty.js');
20
+ }
21
+ return serverOnlyEmptyPath;
22
+ };
32
23
  const resolvePluginPath = (pluginPackage) => {
33
24
  try {
34
25
  return appRequire.resolve(pluginPackage);
@@ -54,6 +45,9 @@ export const loadPluginModule = async (pluginPackage) => {
54
45
  fsCache: true,
55
46
  moduleCache: true,
56
47
  rebuildFsCache: true,
48
+ alias: {
49
+ 'server-only': getServerOnlyEmptyPath(),
50
+ },
57
51
  // Optional: nicer debugging in stack traces (off by default in jiti)
58
52
  // sourceMaps: true,
59
53
  });
@@ -63,38 +57,66 @@ export const loadPluginModule = async (pluginPackage) => {
63
57
  const mod = jitiInstance(resolved);
64
58
  return mod?.default ?? mod;
65
59
  };
66
- function resolvePluginInstance(registration, mod) {
67
- const candidate = mod.createPlugin
60
+ async function resolvePluginInstance(registration, mod) {
61
+ const candidate = await (mod.createPlugin
68
62
  ? mod.createPlugin(registration.options)
69
63
  : typeof mod.default === 'function'
70
64
  ? mod.default()
71
- : (mod.default ?? mod.plugin);
65
+ : (mod.default ?? mod.plugin));
72
66
  if (!candidate) {
73
- console.warn(`[plugins] Plugin "${registration.package}" did not return a plugin instance`);
67
+ logWarn('plugins', `Plugin "${registration.package}" did not return a plugin instance.`);
74
68
  return null;
75
69
  }
76
- if (!candidate.name || !candidate.title) {
77
- console.warn(`[plugins] Plugin "${registration.package}" is missing required "name" or "title". Skipping.`);
70
+ if (!candidate.title) {
71
+ logWarn('plugins', `Plugin "${registration.package}" is missing required "title". Skipping.`);
78
72
  return null;
79
73
  }
80
- // Apply config-level title override if provided
81
- const resolvedTitle = registration.title ?? candidate.title;
82
- // Apply route-level title overrides if provided
83
- // For single-route plugins, the config-level title also applies to the route
84
- const isSingleRoute = (candidate.routes?.length ?? 0) <= 1;
85
- const resolvedRoutes = (candidate.routes ?? []).map((route) => {
86
- const routeOverride = registration.routes?.[route.path];
87
- // Priority: route-specific override > config title (single-route only) > route default
88
- const effectiveTitle = routeOverride?.title ?? (isSingleRoute ? registration.title : undefined) ?? route.title;
89
- return {
90
- ...route,
91
- title: effectiveTitle,
92
- };
74
+ const name = derivePluginName(registration.package);
75
+ const pkg = registration.package;
76
+ const override = registration.override;
77
+ const declaredRoutes = candidate.routes ?? [];
78
+ const isSingleRoute = declaredRoutes.length <= 1;
79
+ const groupRoutes = registration.groupRoutes ?? true;
80
+ // Validate override.routes keys against declared paths
81
+ if (override?.routes) {
82
+ const declaredPaths = new Set(declaredRoutes.map((r) => r.path));
83
+ for (const path of Object.keys(override.routes)) {
84
+ if (!declaredPaths.has(path)) {
85
+ const known = Array.from(declaredPaths).join(', ') || '(none)';
86
+ logWarn('plugins', `override.routes["${path}"] in "${pkg}" does not match any declared route. Known paths: ${known}`);
87
+ }
88
+ }
89
+ }
90
+ // Warn on ineffective override shapes
91
+ if (!isSingleRoute && groupRoutes === false && (override?.title || override?.icon)) {
92
+ logWarn('plugins', `override.title / override.icon set for "${pkg}" but groupRoutes is false — group label is not rendered, so these have no effect.`);
93
+ }
94
+ // Resolve route titles/icons
95
+ const resolvedRoutes = declaredRoutes.map((route) => {
96
+ let title = route.title;
97
+ let icon = route.icon;
98
+ // Single-route: plugin-level override applies to the route itself.
99
+ if (isSingleRoute) {
100
+ if (override?.title)
101
+ title = override.title;
102
+ if (override?.icon)
103
+ icon = override.icon;
104
+ }
105
+ // Per-route override always wins over plugin-level.
106
+ const routeOverride = override?.routes?.[route.path];
107
+ if (routeOverride?.title)
108
+ title = routeOverride.title;
109
+ if (routeOverride?.icon)
110
+ icon = routeOverride.icon;
111
+ return { ...route, title, icon };
93
112
  });
113
+ // Resolve plugin-level title/icon — only used as group label for multi-route grouped plugins.
114
+ const resolvedPluginTitle = !isSingleRoute && override?.title ? override.title : candidate.title;
115
+ const resolvedPluginIcon = !isSingleRoute && override?.icon ? override.icon : candidate.icon;
94
116
  return {
95
- title: resolvedTitle,
96
- name: candidate.name,
97
- registryName: candidate.registryName,
117
+ title: resolvedPluginTitle,
118
+ icon: resolvedPluginIcon,
119
+ name,
98
120
  version: candidate.version,
99
121
  router: candidate.router,
100
122
  routes: resolvedRoutes,
@@ -113,45 +135,42 @@ export async function loadPlugins() {
113
135
  const config = await getCMSConfig();
114
136
  const registrations = config.plugins ?? [];
115
137
  const seenPackages = new Set();
116
- const seenRouterKeys = new Set();
138
+ const seenNames = new Set();
117
139
  const loaded = [];
118
140
  for (const registration of registrations) {
119
141
  if (!registration.package) {
120
- console.warn(`[plugins] Plugin registration is missing "package" field.`);
121
- console.warn(`[plugins] Please check your cms.config.ts file.`);
122
- console.warn(`[plugins] Skipping this plugin registration.`);
142
+ logWarn('plugins', 'Plugin registration is missing "package" field — check your cms.config.ts. Skipping.');
143
+ continue;
144
+ }
145
+ if (seenPackages.has(registration.package)) {
146
+ logError('plugins', `Duplicate plugin package "${registration.package}" registered. Skipping duplicate.`);
123
147
  continue;
124
148
  }
125
149
  const mod = await loadPluginModule(registration.package);
126
150
  if (!mod)
127
151
  continue;
128
- const plugin = resolvePluginInstance(registration, mod);
152
+ const plugin = await resolvePluginInstance(registration, mod);
129
153
  if (!plugin)
130
154
  continue;
131
- const routerKey = deriveRouterKey(registration.package);
132
- if (seenPackages.has(registration.package)) {
133
- console.error(`[plugins] Duplicate plugin package "${registration.package}" registered. Skipping duplicate.`);
134
- continue;
135
- }
136
- if (seenRouterKeys.has(routerKey)) {
137
- console.error(`[plugins] Duplicate plugin router key "${routerKey}" derived from "${registration.package}". Skipping.`);
155
+ if (seenNames.has(plugin.name)) {
156
+ logError('plugins', `Duplicate plugin name "${plugin.name}" derived from "${registration.package}". Skipping.`);
138
157
  continue;
139
158
  }
140
159
  seenPackages.add(registration.package);
141
- seenRouterKeys.add(routerKey);
160
+ seenNames.add(plugin.name);
142
161
  if (typeof plugin.init === 'function') {
143
162
  try {
144
163
  await plugin.init({ config: registration });
145
164
  }
146
165
  catch (error) {
147
- console.error(`[plugins] Plugin "${registration.package}" init failed:`, error);
166
+ logError('plugins', `Plugin "${registration.package}" init failed.`, error);
148
167
  }
149
168
  }
150
169
  loaded.push({
151
170
  config: registration,
152
171
  plugin,
153
172
  moduleName: registration.package,
154
- routerKey,
173
+ routerKey: derivePluginRouterKey(registration.package),
155
174
  });
156
175
  }
157
176
  cachedPlugins = loaded;
@@ -172,17 +191,16 @@ export async function getPluginRoutes() {
172
191
  const requireComponent = routes.length > 1;
173
192
  return routes.map((route) => {
174
193
  if (requireComponent && !route.component) {
175
- console.warn(chalk.red(`[plugins] Route "${route.path}" from "${entry.config.package}" should set "component" because the plugin defines multiple routes.`));
194
+ logWarn('plugins', `Route "${route.path}" from "${entry.config.package}" should set "component" because the plugin defines multiple routes.`);
176
195
  }
177
196
  if (!requireComponent && route.component) {
178
- console.warn(chalk.gray(`[plugins] Route "${route.path}" from "${entry.config.package}" should omit "component" and export a default component, because the plugin defines a single route.`));
197
+ logInfo('plugins', `Route "${route.path}" from "${entry.config.package}" should omit "component" and export a default component, because the plugin defines a single route.`);
179
198
  }
180
199
  return {
181
200
  ...route,
182
201
  title: route.title,
183
202
  pluginId: entry.config.package,
184
203
  pluginName: entry.plugin.name,
185
- pluginRegistryName: entry.plugin.registryName,
186
204
  };
187
205
  });
188
206
  });
@@ -192,6 +210,45 @@ export async function findPluginRouteByPath(path) {
192
210
  const routes = await getPluginRoutes();
193
211
  return routes.find((route) => route.path === normalized) ?? null;
194
212
  }
213
+ /**
214
+ * Returns the navigation structure for all loaded plugins, accounting for
215
+ * `groupRoutes` and per-plugin `override` resolution. Single-route plugins emit
216
+ * one leaf. Multi-route plugins emit a group (default) or one leaf per route
217
+ * when `groupRoutes: false`.
218
+ *
219
+ * Pure data — does not filter by privilege. The sidebar layer applies that.
220
+ */
221
+ export async function getPluginNavigation() {
222
+ const loaded = await loadPlugins();
223
+ const items = [];
224
+ for (const entry of loaded) {
225
+ const routes = entry.plugin.routes ?? [];
226
+ if (routes.length === 0)
227
+ continue;
228
+ const isSingleRoute = routes.length === 1;
229
+ const groupRoutes = entry.config.groupRoutes ?? true;
230
+ if (isSingleRoute || !groupRoutes) {
231
+ for (const route of routes) {
232
+ items.push({
233
+ kind: 'leaf',
234
+ title: route.title,
235
+ path: route.path,
236
+ icon: route.icon,
237
+ pluginName: entry.plugin.name,
238
+ });
239
+ }
240
+ continue;
241
+ }
242
+ items.push({
243
+ kind: 'group',
244
+ title: entry.plugin.title,
245
+ icon: entry.plugin.icon,
246
+ pluginName: entry.plugin.name,
247
+ children: routes.map((r) => ({ title: r.title, path: r.path, icon: r.icon })),
248
+ });
249
+ }
250
+ return items;
251
+ }
195
252
  /**
196
253
  * Gets the dashboard override plugin route if configured.
197
254
  * @returns The LoadedPluginRoute for the dashboard override, or null if not configured
@@ -0,0 +1,28 @@
1
+ import type { PluginConfigEntry } from '../core/config/config-loader.js';
2
+ import type { MultilingualString } from '../translations/language-utils.js';
3
+ import type { IconName } from 'lucide-react/dynamic';
4
+ export interface PluginManifestRoute {
5
+ path: string;
6
+ title: MultilingualString;
7
+ icon?: IconName;
8
+ component?: string;
9
+ }
10
+ export interface PluginManifest {
11
+ name: string;
12
+ registryName: string;
13
+ title: MultilingualString;
14
+ routes?: PluginManifestRoute[];
15
+ }
16
+ export interface LoadedPluginManifestRoute extends PluginManifestRoute {
17
+ pluginId: string;
18
+ pluginName: string;
19
+ pluginRegistryName: string;
20
+ }
21
+ export interface LoadedPluginManifest {
22
+ config: PluginConfigEntry;
23
+ manifest: PluginManifest;
24
+ moduleName: string;
25
+ }
26
+ export declare function getPluginManifests(): Promise<LoadedPluginManifest[]>;
27
+ export declare function getPluginRouteManifests(): Promise<LoadedPluginManifestRoute[]>;
28
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/plugins/manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAEpD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,kBAAkB,CAAA;IACzB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,kBAAkB,CAAA;IACzB,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAA;CACjC;AAED,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;CACrB;AAoDD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA+B1E;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAUpF"}
@@ -0,0 +1,83 @@
1
+ import { createRequire } from 'module';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { getCMSConfig } from '../core/config/index.js';
5
+ const appRequire = createRequire(path.join(process.cwd(), 'package.json'));
6
+ const pkgRequire = createRequire(import.meta.url);
7
+ const resolvePluginPackageJson = (pluginPackage) => {
8
+ const target = `${pluginPackage}/package.json`;
9
+ try {
10
+ return appRequire.resolve(target);
11
+ }
12
+ catch {
13
+ try {
14
+ return pkgRequire.resolve(target, { paths: [process.cwd()] });
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ };
21
+ const readManifestFromPackage = (pluginPackage) => {
22
+ const pkgJsonPath = resolvePluginPackageJson(pluginPackage);
23
+ if (!pkgJsonPath) {
24
+ console.warn(`[plugins] Could not resolve "${pluginPackage}/package.json".`);
25
+ return null;
26
+ }
27
+ let pkg;
28
+ try {
29
+ pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
30
+ }
31
+ catch (error) {
32
+ console.warn(`[plugins] Failed to read "${pkgJsonPath}":`, error);
33
+ return null;
34
+ }
35
+ const manifest = pkg.pluginManifest;
36
+ if (!manifest) {
37
+ console.warn(`[plugins] Package "${pluginPackage}" is missing the "pluginManifest" field in package.json. ` +
38
+ `Plugins must declare metadata statically so the CLI can read it without evaluating plugin code.`);
39
+ return null;
40
+ }
41
+ if (!manifest.name || !manifest.registryName || !manifest.title) {
42
+ console.warn(`[plugins] Package "${pluginPackage}" has an invalid "pluginManifest" — required fields: name, registryName, title.`);
43
+ return null;
44
+ }
45
+ return manifest;
46
+ };
47
+ export async function getPluginManifests() {
48
+ const config = await getCMSConfig();
49
+ const registrations = config.plugins ?? [];
50
+ const seenPackages = new Set();
51
+ const seenNames = new Set();
52
+ const loaded = [];
53
+ for (const registration of registrations) {
54
+ if (!registration.package)
55
+ continue;
56
+ if (seenPackages.has(registration.package))
57
+ continue;
58
+ const manifest = readManifestFromPackage(registration.package);
59
+ if (!manifest)
60
+ continue;
61
+ if (seenNames.has(manifest.name)) {
62
+ console.warn(`[plugins] Duplicate plugin name "${manifest.name}" — skipping "${registration.package}".`);
63
+ continue;
64
+ }
65
+ seenPackages.add(registration.package);
66
+ seenNames.add(manifest.name);
67
+ loaded.push({
68
+ config: registration,
69
+ manifest,
70
+ moduleName: registration.package,
71
+ });
72
+ }
73
+ return loaded;
74
+ }
75
+ export async function getPluginRouteManifests() {
76
+ const loaded = await getPluginManifests();
77
+ return loaded.flatMap((entry) => (entry.manifest.routes ?? []).map((route) => ({
78
+ ...route,
79
+ pluginId: entry.config.package,
80
+ pluginName: entry.manifest.name,
81
+ pluginRegistryName: entry.manifest.registryName,
82
+ })));
83
+ }
@@ -0,0 +1,16 @@
1
+ import type { LoadedPluginRoute } from './loader.js';
2
+ /**
3
+ * Walks a dot-path into a tRPC RSC `api` proxy and invokes `.prefetch()` at the leaf.
4
+ *
5
+ * Example: `runApiPrefetch(api, 'cpanelEmails.getEmails')` runs
6
+ * `await api.cpanelEmails.getEmails.prefetch()`.
7
+ *
8
+ * Throws if any segment is missing or the leaf has no `prefetch` helper, so a typo
9
+ * in `PluginRoute.prefetch` fails loudly during development.
10
+ */
11
+ export declare function runApiPrefetch(api: unknown, dotPath: string): Promise<void>;
12
+ /** Normalizes a route's `prefetch` field (string | string[] | undefined) into an array. */
13
+ export declare function getRoutePrefetchPaths(route: Pick<LoadedPluginRoute, 'prefetch'>): string[];
14
+ /** Runs all declared prefetches for a route in parallel. */
15
+ export declare function runRoutePrefetches(api: unknown, route: Pick<LoadedPluginRoute, 'prefetch'>): Promise<void>;
16
+ //# sourceMappingURL=prefetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefetch.d.ts","sourceRoot":"","sources":["../../src/plugins/prefetch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjF;AAED,2FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG,MAAM,EAAE,CAG1F;AAED,4DAA4D;AAC5D,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhH"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Walks a dot-path into a tRPC RSC `api` proxy and invokes `.prefetch()` at the leaf.
3
+ *
4
+ * Example: `runApiPrefetch(api, 'cpanelEmails.getEmails')` runs
5
+ * `await api.cpanelEmails.getEmails.prefetch()`.
6
+ *
7
+ * Throws if any segment is missing or the leaf has no `prefetch` helper, so a typo
8
+ * in `PluginRoute.prefetch` fails loudly during development.
9
+ */
10
+ export async function runApiPrefetch(api, dotPath) {
11
+ const segments = dotPath.split('.').filter(Boolean);
12
+ if (segments.length === 0) {
13
+ throw new Error('[plugins] Empty prefetch path.');
14
+ }
15
+ let node = api;
16
+ for (const key of segments) {
17
+ if (node == null || (typeof node !== 'object' && typeof node !== 'function')) {
18
+ throw new Error(`[plugins] Cannot resolve prefetch path "${dotPath}" — segment "${key}" is unreachable.`);
19
+ }
20
+ node = node[key];
21
+ }
22
+ const prefetch = node?.prefetch;
23
+ if (typeof prefetch !== 'function') {
24
+ throw new Error(`[plugins] Prefetch path "${dotPath}" does not resolve to a procedure with a prefetch helper.`);
25
+ }
26
+ await prefetch();
27
+ }
28
+ /** Normalizes a route's `prefetch` field (string | string[] | undefined) into an array. */
29
+ export function getRoutePrefetchPaths(route) {
30
+ if (!route.prefetch)
31
+ return [];
32
+ return Array.isArray(route.prefetch) ? route.prefetch : [route.prefetch];
33
+ }
34
+ /** Runs all declared prefetches for a route in parallel. */
35
+ export async function runRoutePrefetches(api, route) {
36
+ const paths = getRoutePrefetchPaths(route);
37
+ if (paths.length === 0)
38
+ return;
39
+ await Promise.all(paths.map((path) => runApiPrefetch(api, path)));
40
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Static import map for all official @nextjscms plugins.
3
+ *
4
+ * Why this file exists: the plugin loader needs to import plugin packages by
5
+ * name at runtime. Bare `await import(variable)` calls are rejected by
6
+ * Turbopack/webpack as "too dynamic" because the bundler cannot statically
7
+ * see which packages may be loaded. Listing each package as a literal
8
+ * `() => import('@nextjscms/plugin-x')` thunk lets the bundler analyze the
9
+ * import sites, and the host's module loader applies the correct export
10
+ * conditions at runtime — most importantly "react-server" during RSC
11
+ * requests, so plugin transitive deps marked `import 'server-only'` resolve
12
+ * to a no-op instead of throwing.
13
+ *
14
+ * Each thunk is wrapped in its own arrow so the import is deferred until the
15
+ * plugin is actually configured in cms.config.ts. Plugins not installed in
16
+ * the host app will fail at thunk invocation, which the loader treats as a
17
+ * normal "plugin not available" outcome.
18
+ *
19
+ * When a new official plugin is published, add an entry here.
20
+ */
21
+ export declare const officialPluginRegistry: Record<string, () => Promise<unknown>>;
22
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAIzE,CAAA"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Static import map for all official @nextjscms plugins.
3
+ *
4
+ * Why this file exists: the plugin loader needs to import plugin packages by
5
+ * name at runtime. Bare `await import(variable)` calls are rejected by
6
+ * Turbopack/webpack as "too dynamic" because the bundler cannot statically
7
+ * see which packages may be loaded. Listing each package as a literal
8
+ * `() => import('@nextjscms/plugin-x')` thunk lets the bundler analyze the
9
+ * import sites, and the host's module loader applies the correct export
10
+ * conditions at runtime — most importantly "react-server" during RSC
11
+ * requests, so plugin transitive deps marked `import 'server-only'` resolve
12
+ * to a no-op instead of throwing.
13
+ *
14
+ * Each thunk is wrapped in its own arrow so the import is deferred until the
15
+ * plugin is actually configured in cms.config.ts. Plugins not installed in
16
+ * the host app will fail at thunk invocation, which the loader treats as a
17
+ * normal "plugin not available" outcome.
18
+ *
19
+ * When a new official plugin is published, add an entry here.
20
+ */
21
+ export const officialPluginRegistry = {
22
+ '@nextjscms/plugin-cpanel-dashboard': () => import('@nextjscms/plugin-cpanel-dashboard'),
23
+ '@nextjscms/plugin-cpanel-emails': () => import('@nextjscms/plugin-cpanel-emails'),
24
+ '@nextjscms/plugin-google-analytics': () => import('@nextjscms/plugin-google-analytics'),
25
+ };
@@ -1,2 +1,4 @@
1
1
  export * from './loader.js';
2
+ export { derivePluginName, derivePluginRouterKey, extractPluginName, PLUGIN_PACKAGE_PREFIX } from './derive.js';
3
+ export { runApiPrefetch, runRoutePrefetches, getRoutePrefetchPaths } from './prefetch.js';
2
4
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/plugins/server.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/plugins/server.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC/G,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
@@ -1 +1,3 @@
1
1
  export * from './loader.js';
2
+ export { derivePluginName, derivePluginRouterKey, extractPluginName, PLUGIN_PACKAGE_PREFIX } from './derive.js';
3
+ export { runApiPrefetch, runRoutePrefetches, getRoutePrefetchPaths } from './prefetch.js';
@@ -0,0 +1,79 @@
1
+ import type { IconName } from 'lucide-react/dynamic';
2
+ import type { MultilingualString } from '../translations/language-utils.js';
3
+ export interface PluginRouteOverride {
4
+ title?: MultilingualString;
5
+ icon?: IconName;
6
+ }
7
+ export interface PluginRuntimeConfigEntry {
8
+ package: string;
9
+ options?: Record<string, unknown>;
10
+ override?: {
11
+ title?: MultilingualString;
12
+ icon?: IconName;
13
+ routes?: Record<string, PluginRouteOverride>;
14
+ };
15
+ groupRoutes?: boolean;
16
+ }
17
+ export interface PluginInitContext {
18
+ config: PluginRuntimeConfigEntry;
19
+ }
20
+ export interface PluginRoute {
21
+ path: string;
22
+ /**
23
+ * The title of the route.
24
+ * Can be a plain string or a localized object with language codes as keys.
25
+ */
26
+ title: MultilingualString;
27
+ icon?: IconName | undefined;
28
+ component?: string;
29
+ /**
30
+ * Dot-paths into the host's tRPC router (e.g. `'cpanelEmails.getEmails'`) that the
31
+ * host should prefetch on the server before rendering the plugin's component. The
32
+ * results are placed into the React Query cache and hydrated into the client tree
33
+ * via the host-rendered `<HydrateClient>` boundary.
34
+ */
35
+ prefetch?: string | string[];
36
+ }
37
+ export type PluginRouteWithoutComponent = Omit<PluginRoute, 'component'> & {
38
+ component?: never;
39
+ };
40
+ export type PluginRouteWithComponent = PluginRoute & {
41
+ component: string;
42
+ };
43
+ export interface CMSPlugin {
44
+ /**
45
+ * The human facing title of the plugin.
46
+ * Can be a plain string or a localized object with language codes as keys.
47
+ * @example 'Some Plugin Title'
48
+ * @example { en: 'Some Plugin Title', ar: 'عنوان الإضافة' }
49
+ */
50
+ title: MultilingualString;
51
+ /**
52
+ * Optional sidebar icon for the plugin group (multi-route, when `groupRoutes !== false`).
53
+ * Single-route plugins use the route's own `icon` instead.
54
+ */
55
+ icon?: IconName;
56
+ version?: string;
57
+ router?: unknown;
58
+ routes?: readonly PluginRoute[];
59
+ init?: (ctx: PluginInitContext) => Promise<void> | void;
60
+ }
61
+ type CMSPluginBase = Omit<CMSPlugin, 'routes'>;
62
+ export type CMSPluginSingleRoute = CMSPluginBase & {
63
+ routes?: readonly [] | readonly [PluginRouteWithoutComponent];
64
+ };
65
+ export type CMSPluginMultiRoute = CMSPluginBase & {
66
+ routes: readonly [PluginRouteWithComponent, PluginRouteWithComponent, ...PluginRouteWithComponent[]];
67
+ };
68
+ export type CMSPluginDescriptorBase = Omit<CMSPluginBase, 'router' | 'init'> & {
69
+ package: string;
70
+ };
71
+ export type CMSPluginDescriptorSingleRoute = CMSPluginDescriptorBase & {
72
+ routes?: readonly [] | readonly [PluginRouteWithoutComponent];
73
+ };
74
+ export type CMSPluginDescriptorMultiRoute = CMSPluginDescriptorBase & {
75
+ routes: readonly [PluginRouteWithComponent, PluginRouteWithComponent, ...PluginRouteWithComponent[]];
76
+ };
77
+ export type CMSPluginDescriptor = CMSPluginDescriptorSingleRoute | CMSPluginDescriptorMultiRoute;
78
+ export {};
79
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAE3E,MAAM,WAAW,mBAAmB;IAChC,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAC1B,IAAI,CAAC,EAAE,QAAQ,CAAA;CAClB;AAED,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,kBAAkB,CAAA;QAC1B,IAAI,CAAC,EAAE,QAAQ,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;KAC/C,CAAA;IACD,WAAW,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,wBAAwB,CAAA;CACnC;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,KAAK,EAAE,kBAAkB,CAAA;IACzB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC/B;AAED,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAEhG,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1E,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,KAAK,EAAE,kBAAkB,CAAA;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IAC/B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC1D;AAED,KAAK,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;AAE9C,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG;IAC/C,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,2BAA2B,CAAC,CAAA;CAChE,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG;IAC9C,MAAM,EAAE,SAAS,CAAC,wBAAwB,EAAE,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAAC,CAAA;CACvG,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC,GAAG;IAC3E,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG,uBAAuB,GAAG;IACnE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,2BAA2B,CAAC,CAAA;CAChE,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG,uBAAuB,GAAG;IAClE,MAAM,EAAE,SAAS,CAAC,wBAAwB,EAAE,wBAAwB,EAAE,GAAG,wBAAwB,EAAE,CAAC,CAAA;CACvG,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,8BAA8B,GAAG,6BAA6B,CAAA"}
File without changes
@@ -369,6 +369,7 @@ declare const _default: {
369
369
  readonly addedAgo: "Added {time} ago";
370
370
  readonly hoursAgo: "hours ago";
371
371
  readonly main: "Main";
372
+ readonly plugins: "Plugins";
372
373
  readonly categorySections: "Category Sections";
373
374
  readonly sectionsWithItems: "Sections with Items";
374
375
  readonly simpleSections: "Simple Sections";
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/translations/base/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAogBU"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../src/translations/base/en.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wBAqgBU"}
@@ -369,6 +369,7 @@ export default {
369
369
  addedAgo: 'Added {time} ago',
370
370
  hoursAgo: 'hours ago',
371
371
  main: 'Main',
372
+ plugins: 'Plugins',
372
373
  categorySections: 'Category Sections',
373
374
  sectionsWithItems: 'Sections with Items',
374
375
  simpleSections: 'Simple Sections',