@nuasite/cms 0.43.0-beta.2 → 0.43.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.js +1 -1
- package/package.json +13 -5
- package/src/local-admin.ts +90 -6
- package/src/tsconfig.json +0 -1
package/dist/editor.js
CHANGED
|
@@ -386,7 +386,7 @@ function fC(t, e) {
|
|
|
386
386
|
function mC(t, e) {
|
|
387
387
|
return typeof e == "function" ? e(t) : e;
|
|
388
388
|
}
|
|
389
|
-
const W3 = "0.43.0-beta.
|
|
389
|
+
const W3 = "0.43.0-beta.3", K3 = W3, ct = {
|
|
390
390
|
/** Highlight overlay for hovered elements */
|
|
391
391
|
HIGHLIGHT: 2147483644,
|
|
392
392
|
/** Hover outline for elements/components */
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"directory": "packages/astro-cms"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
|
-
"version": "0.43.0-beta.
|
|
17
|
+
"version": "0.43.0-beta.3",
|
|
18
18
|
"module": "src/index.ts",
|
|
19
19
|
"types": "src/index.ts",
|
|
20
20
|
"type": "module",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@nuasite/cms-core": "0.43.0-beta.
|
|
30
|
-
"@nuasite/cms-types": "0.43.0-beta.
|
|
31
|
-
"@nuasite/cms-sidecar": "0.43.0-beta.2",
|
|
32
|
-
"@nuasite/collections-admin": "0.43.0-beta.2",
|
|
29
|
+
"@nuasite/cms-core": "0.43.0-beta.3",
|
|
30
|
+
"@nuasite/cms-types": "0.43.0-beta.3",
|
|
33
31
|
"@astrojs/compiler": "^3.0.1",
|
|
34
32
|
"@babel/parser": "^7.29.2",
|
|
35
33
|
"node-html-parser": "^7.1.0",
|
|
36
34
|
"yaml": "^2.8.3"
|
|
37
35
|
},
|
|
38
36
|
"devDependencies": {
|
|
37
|
+
"@nuasite/cms-sidecar": "0.43.0-beta.3",
|
|
38
|
+
"@nuasite/collections-admin": "0.43.0-beta.3",
|
|
39
39
|
"@babel/types": "^7.29.0",
|
|
40
40
|
"@types/react": "^19.2.7",
|
|
41
41
|
"@types/react-dom": "^19.2.3",
|
|
@@ -72,6 +72,8 @@
|
|
|
72
72
|
"typescript": "^6.0.2",
|
|
73
73
|
"vite": "^7.0.0",
|
|
74
74
|
"@aws-sdk/client-s3": "^3.0.0",
|
|
75
|
+
"@nuasite/cms-sidecar": "0.43.0-beta.3",
|
|
76
|
+
"@nuasite/collections-admin": "0.43.0-beta.3",
|
|
75
77
|
"react": "^19.0.0",
|
|
76
78
|
"react-dom": "^19.0.0"
|
|
77
79
|
},
|
|
@@ -79,6 +81,12 @@
|
|
|
79
81
|
"@aws-sdk/client-s3": {
|
|
80
82
|
"optional": true
|
|
81
83
|
},
|
|
84
|
+
"@nuasite/cms-sidecar": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"@nuasite/collections-admin": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
82
90
|
"react": {
|
|
83
91
|
"optional": true
|
|
84
92
|
},
|
package/src/local-admin.ts
CHANGED
|
@@ -25,7 +25,13 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { createCmsCore, createNodeFs } from '@nuasite/cms-core'
|
|
28
|
-
|
|
28
|
+
// `@nuasite/cms-sidecar` is an OPTIONAL peer (cms-headless F6 slim): a generated
|
|
29
|
+
// site that depends on `@nuasite/cms` must not pull the sidecar (+ collections-admin
|
|
30
|
+
// + React) into its resolved tree. The TYPES are imported `import type` (erased at
|
|
31
|
+
// build, so no runtime dependency edge), and the VALUES are loaded lazily via a
|
|
32
|
+
// dynamic `import()` only when local mode actually serves `/_nua/admin`. Hosted
|
|
33
|
+
// (sandbox) mode never registers this middleware, so it never imports the peer.
|
|
34
|
+
import type { CmsSidecarServer, createServer as CreateSidecarServer } from '@nuasite/cms-sidecar'
|
|
29
35
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
30
36
|
import { getProjectRoot } from './config'
|
|
31
37
|
import { readBody } from './handlers/request-utils'
|
|
@@ -50,6 +56,13 @@ export interface LocalAdminOptions {
|
|
|
50
56
|
maxUploadSize: number
|
|
51
57
|
/** Virtual-module id of the SPA entry the HTML shell loads (transformed by Vite). */
|
|
52
58
|
entryModuleId: string
|
|
59
|
+
/**
|
|
60
|
+
* Loader for the optional peers (cms-headless F6). Defaults to the real
|
|
61
|
+
* dynamic-`import()` loader; injectable so a test can simulate the peers being
|
|
62
|
+
* absent. The peers are loaded lazily on first `/_nua/admin` hit, never on
|
|
63
|
+
* `pletivo dev` startup.
|
|
64
|
+
*/
|
|
65
|
+
peerLoader?: AdminPeerLoader
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
/** URL the collections-admin SPA is served at. */
|
|
@@ -61,6 +74,54 @@ export const ADMIN_API_PREFIX = '/_nua/cms-admin-api'
|
|
|
61
74
|
/** `apiBase` passed to the SPA — the sidecar serves its routes under `/cms/v1`. */
|
|
62
75
|
export const ADMIN_API_BASE = `${ADMIN_API_PREFIX}/cms/v1`
|
|
63
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Message shown when local mode tries to open `/_nua/admin` but the optional
|
|
79
|
+
* peers (`@nuasite/cms-sidecar` + `@nuasite/collections-admin`) are not installed.
|
|
80
|
+
* The marker pipeline + inline editing keep working — only the full-page admin is
|
|
81
|
+
* unavailable until the peers are added (or the site is run via pletivo).
|
|
82
|
+
*/
|
|
83
|
+
export const ADMIN_PEERS_MISSING_MESSAGE =
|
|
84
|
+
'Local CMS admin requires @nuasite/cms-sidecar and @nuasite/collections-admin — install them (or run via pletivo) to enable /_nua/admin.'
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Loader seam for the two optional peers (cms-headless F6). The default
|
|
88
|
+
* implementation loads them lazily via dynamic `import()` — the only dynamic
|
|
89
|
+
* imports in this package (user-approved) — keeping them out of a slim generated
|
|
90
|
+
* site's resolved tree. Injectable so a test can simulate the peers being absent
|
|
91
|
+
* without uninstalling them from the workspace.
|
|
92
|
+
*/
|
|
93
|
+
export interface AdminPeerLoader {
|
|
94
|
+
/** Resolve `@nuasite/cms-sidecar`'s `createServer`, or `null` if the peer is absent. */
|
|
95
|
+
loadCreateSidecar(): Promise<typeof CreateSidecarServer | null>
|
|
96
|
+
/** Whether `@nuasite/collections-admin` (the SPA the shell mounts) is installed. */
|
|
97
|
+
isCollectionsAdminInstalled(): Promise<boolean>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Default peer loader: lazily `import()` the optional peers on first use. The
|
|
102
|
+
* module ids are bound to `const`s so they are NOT static-import string literals —
|
|
103
|
+
* a slim site that doesn't install the peers never resolves them, and the import
|
|
104
|
+
* only runs in local mode on the first `/_nua/admin` (or admin-api) hit. Both
|
|
105
|
+
* rejections (peer absent) degrade to `null` / `false` rather than throwing.
|
|
106
|
+
*/
|
|
107
|
+
export const defaultAdminPeerLoader: AdminPeerLoader = {
|
|
108
|
+
async loadCreateSidecar() {
|
|
109
|
+
const moduleId = '@nuasite/cms-sidecar'
|
|
110
|
+
const loaded = await import(moduleId).then(
|
|
111
|
+
(mod: { createServer: typeof CreateSidecarServer }) => mod,
|
|
112
|
+
() => null,
|
|
113
|
+
)
|
|
114
|
+
return loaded === null ? null : loaded.createServer
|
|
115
|
+
},
|
|
116
|
+
async isCollectionsAdminInstalled() {
|
|
117
|
+
const moduleId = '@nuasite/collections-admin'
|
|
118
|
+
return import(moduleId).then(
|
|
119
|
+
() => true,
|
|
120
|
+
() => false,
|
|
121
|
+
)
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
64
125
|
/**
|
|
65
126
|
* Build the HTML shell for the admin SPA. It loads the virtual entry module
|
|
66
127
|
* (which imports the lib's stylesheet and mounts `<CollectionsAdminApp apiBase={…} />`),
|
|
@@ -93,11 +154,17 @@ function adminShellHtml(entryModuleId: string, apiBase: string): string {
|
|
|
93
154
|
* Lazily create the in-process cms-sidecar over the project's `node:fs`. The core
|
|
94
155
|
* is built once on first use and reused; the media adapter mirrors the dev
|
|
95
156
|
* server's selection (local `public/uploads` by default in this mode).
|
|
157
|
+
*
|
|
158
|
+
* Returns `null` when the optional `@nuasite/cms-sidecar` peer is not installed —
|
|
159
|
+
* callers serve the {@link ADMIN_PEERS_MISSING_MESSAGE} placeholder rather than
|
|
160
|
+
* crashing. The dynamic `import()` of the peer happens here, on first use only.
|
|
96
161
|
*/
|
|
97
|
-
function makeLazySidecar(options: LocalAdminOptions): () => CmsSidecarServer {
|
|
162
|
+
function makeLazySidecar(options: LocalAdminOptions, peerLoader: AdminPeerLoader): () => Promise<CmsSidecarServer | null> {
|
|
98
163
|
let server: CmsSidecarServer | null = null
|
|
99
|
-
return () => {
|
|
164
|
+
return async () => {
|
|
100
165
|
if (server) return server
|
|
166
|
+
const createServer = await peerLoader.loadCreateSidecar()
|
|
167
|
+
if (createServer === null) return null
|
|
101
168
|
const root = getProjectRoot()
|
|
102
169
|
const fs = createNodeFs(root)
|
|
103
170
|
const core = createCmsCore(fs, {
|
|
@@ -161,7 +228,8 @@ async function writeWebResponse(res: ServerResponse, response: Response): Promis
|
|
|
161
228
|
* registers this middleware (verified by the no-op test).
|
|
162
229
|
*/
|
|
163
230
|
export function createLocalAdminMiddleware(server: AdminViteServerLike, options: LocalAdminOptions): void {
|
|
164
|
-
const
|
|
231
|
+
const peerLoader = options.peerLoader ?? defaultAdminPeerLoader
|
|
232
|
+
const getSidecar = makeLazySidecar(options, peerLoader)
|
|
165
233
|
const shell = adminShellHtml(options.entryModuleId, ADMIN_API_BASE)
|
|
166
234
|
|
|
167
235
|
// 1. In-process sidecar API. Strip the local mount prefix and forward the rest
|
|
@@ -176,7 +244,14 @@ export function createLocalAdminMiddleware(server: AdminViteServerLike, options:
|
|
|
176
244
|
|
|
177
245
|
readBody(req, options.maxUploadSize)
|
|
178
246
|
.then(async (body) => {
|
|
179
|
-
const sidecar = getSidecar()
|
|
247
|
+
const sidecar = await getSidecar()
|
|
248
|
+
if (sidecar === null) {
|
|
249
|
+
// Optional peer not installed — degrade gracefully (cms-headless F6).
|
|
250
|
+
res.statusCode = 501
|
|
251
|
+
res.setHeader('content-type', 'application/json; charset=utf-8')
|
|
252
|
+
res.end(JSON.stringify({ error: ADMIN_PEERS_MISSING_MESSAGE, code: 'unsupported' }))
|
|
253
|
+
return
|
|
254
|
+
}
|
|
180
255
|
const request = toWebRequest(req, forwardedPath, body)
|
|
181
256
|
const response = await sidecar.fetch(request)
|
|
182
257
|
await writeWebResponse(res, response)
|
|
@@ -205,7 +280,16 @@ export function createLocalAdminMiddleware(server: AdminViteServerLike, options:
|
|
|
205
280
|
}
|
|
206
281
|
|
|
207
282
|
const serve = async () => {
|
|
208
|
-
const sidecar = getSidecar()
|
|
283
|
+
const sidecar = await getSidecar()
|
|
284
|
+
// Optional peers not installed — serve a clear placeholder rather than a
|
|
285
|
+
// blank/erroring SPA (cms-headless F6). Both halves of the pair are needed:
|
|
286
|
+
// the sidecar (API brain) and collections-admin (the SPA the shell mounts).
|
|
287
|
+
if (sidecar === null || !(await peerLoader.isCollectionsAdminInstalled())) {
|
|
288
|
+
res.statusCode = 501
|
|
289
|
+
res.setHeader('content-type', 'text/plain; charset=utf-8')
|
|
290
|
+
res.end(ADMIN_PEERS_MISSING_MESSAGE)
|
|
291
|
+
return
|
|
292
|
+
}
|
|
209
293
|
const health = await sidecar.fetch(new Request('http://localhost/health'))
|
|
210
294
|
if (!health.ok) {
|
|
211
295
|
res.statusCode = 503
|