@ossy/platform 1.38.1 → 1.38.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/package.json +9 -9
- package/src/PlatformShell.jsx +38 -0
- package/src/server.js +25 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/platform",
|
|
3
|
-
"version": "1.38.
|
|
3
|
+
"version": "1.38.3",
|
|
4
4
|
"description": "Ossy application server runtime",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"@aws-sdk/s3-request-presigner": "^3.1057.0",
|
|
39
39
|
"@aws-sdk/util-create-request": "^3.972.26",
|
|
40
40
|
"@aws-sdk/util-format-url": "^3.972.17",
|
|
41
|
-
"@ossy/event-store": "^1.7.
|
|
42
|
-
"@ossy/observability": "^1.7.
|
|
43
|
-
"@ossy/policies": "^1.12.
|
|
44
|
-
"@ossy/router": "^1.39.
|
|
45
|
-
"@ossy/sdk": "^1.39.
|
|
46
|
-
"@ossy/tokens": "^1.12.
|
|
47
|
-
"@ossy/users": "^1.12.
|
|
41
|
+
"@ossy/event-store": "^1.7.3",
|
|
42
|
+
"@ossy/observability": "^1.7.3",
|
|
43
|
+
"@ossy/policies": "^1.12.3",
|
|
44
|
+
"@ossy/router": "^1.39.3",
|
|
45
|
+
"@ossy/sdk": "^1.39.3",
|
|
46
|
+
"@ossy/tokens": "^1.12.3",
|
|
47
|
+
"@ossy/users": "^1.12.3",
|
|
48
48
|
"cookie-parser": "^1.4.7",
|
|
49
49
|
"dotenv": ">=16.0.0 <18.0.0",
|
|
50
50
|
"express": ">=5.0.0 <6.0.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"src",
|
|
63
63
|
"Dockerfile"
|
|
64
64
|
],
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "501d2273560cb04d668afd32edf448019f3312aa"
|
|
66
66
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ComponentSlotsProvider, Slot } from '@ossy/design-system'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Platform shell — the outermost structural wrapper rendered on every request.
|
|
6
|
+
*
|
|
7
|
+
* Accepts a `slots` prop (plain object of slot-name → ComponentType) built
|
|
8
|
+
* at request time from the manifest's `slots` array. Wraps the entire tree
|
|
9
|
+
* with a `ComponentSlotsProvider` so that all five guaranteed slot regions
|
|
10
|
+
* are available to every descendant.
|
|
11
|
+
*
|
|
12
|
+
* The app's `*.layout.jsx` can nest a second `ComponentSlotsProvider` inside
|
|
13
|
+
* to override or fill these slots with layout-specific components — the inner
|
|
14
|
+
* provider shallow-merges on top.
|
|
15
|
+
*
|
|
16
|
+
* Slot regions rendered here:
|
|
17
|
+
* header — top bar across full width
|
|
18
|
+
* sidebar — left column, full height
|
|
19
|
+
* toolbar — below header, above content (contextual actions)
|
|
20
|
+
* notifications — floating / bottom corner (toasts, badges)
|
|
21
|
+
* system-messages — banner above content (maintenance, errors)
|
|
22
|
+
*
|
|
23
|
+
* @param {{ slots?: Record<string, import('react').ComponentType | null>, children: import('react').ReactNode }} props
|
|
24
|
+
*/
|
|
25
|
+
export function PlatformShell({ slots = {}, children }) {
|
|
26
|
+
return (
|
|
27
|
+
<ComponentSlotsProvider slots={slots}>
|
|
28
|
+
<Slot name="system-messages" />
|
|
29
|
+
<Slot name="header" />
|
|
30
|
+
<Slot name="sidebar" />
|
|
31
|
+
<Slot name="toolbar" />
|
|
32
|
+
{children}
|
|
33
|
+
<Slot name="notifications" />
|
|
34
|
+
</ComponentSlotsProvider>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default PlatformShell
|
package/src/server.js
CHANGED
|
@@ -57,6 +57,7 @@ export function loadManifest (buildDir) {
|
|
|
57
57
|
const apis = entries.filter((e) => e.type === 'api' && validRoutable(e))
|
|
58
58
|
const tasks = entries.filter((e) => e.type === 'task')
|
|
59
59
|
const components = Array.isArray(manifest.components) ? manifest.components : []
|
|
60
|
+
const slots = Array.isArray(manifest.slots) ? manifest.slots : []
|
|
60
61
|
const resourceTemplates = Array.isArray(manifest.resourceTemplates) ? manifest.resourceTemplates : []
|
|
61
62
|
const aggregates = Array.isArray(manifest.aggregates) ? manifest.aggregates : []
|
|
62
63
|
const integrations = Array.isArray(manifest.integrations) ? manifest.integrations : []
|
|
@@ -73,6 +74,7 @@ export function loadManifest (buildDir) {
|
|
|
73
74
|
apis,
|
|
74
75
|
tasks,
|
|
75
76
|
components,
|
|
77
|
+
slots,
|
|
76
78
|
resourceTemplates,
|
|
77
79
|
aggregates,
|
|
78
80
|
integrations,
|
|
@@ -192,6 +194,26 @@ export async function startServer (options = {}) {
|
|
|
192
194
|
}
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
// Slot loading — import each component that declares `export const slot` and
|
|
198
|
+
// build a map of slot-name → entry URL for the render pipeline. Components
|
|
199
|
+
// whose chunk fails to import are silently skipped so a broken slot never
|
|
200
|
+
// prevents the server from starting. The slot entries are appended to
|
|
201
|
+
// `componentEntries` at render time, keyed by slot name, so that the App's
|
|
202
|
+
// ComponentSlotsProvider makes them available to the entire React tree.
|
|
203
|
+
// Layout-level ComponentSlotsProviders can merge on top to override slots
|
|
204
|
+
// for their subtree (e.g. layout fills `header` with brand-specific chrome).
|
|
205
|
+
/** @type {Array<{ id: string, entry: string }>} */
|
|
206
|
+
const slotComponentEntries = []
|
|
207
|
+
for (const slotEntry of manifest.slots ?? []) {
|
|
208
|
+
try {
|
|
209
|
+
// Verify the chunk is importable at startup so we fail fast on bad builds.
|
|
210
|
+
await import(resolveEntryUrl(slotEntry.entry, buildDir))
|
|
211
|
+
slotComponentEntries.push({ id: slotEntry.slot, entry: slotEntry.entry })
|
|
212
|
+
} catch (err) {
|
|
213
|
+
log.warn(`Failed to load slot component "${slotEntry.slot}"`, undefined, err)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
195
217
|
function findLayoutForPage (pagePathOrPaths) {
|
|
196
218
|
if (!layoutModuleMap.size) return null
|
|
197
219
|
const paths = typeof pagePathOrPaths === 'string'
|
|
@@ -335,7 +357,9 @@ export async function startServer (options = {}) {
|
|
|
335
357
|
isAuthenticated: !!req.isAuthenticated,
|
|
336
358
|
pages: manifest.pages.map((page) => ({ id: page.id, path: page.path })),
|
|
337
359
|
pageId: pageRoute.id,
|
|
338
|
-
|
|
360
|
+
// Slot component entries are prepended so that layout-level
|
|
361
|
+
// ComponentSlotsProviders (which merge on top) can override them.
|
|
362
|
+
componentEntries: [...slotComponentEntries, ...manifest.components],
|
|
339
363
|
Layout: Layout ?? null,
|
|
340
364
|
}
|
|
341
365
|
const html = await mod.render(props)
|