@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/platform",
3
- "version": "1.38.1",
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.1",
42
- "@ossy/observability": "^1.7.1",
43
- "@ossy/policies": "^1.12.1",
44
- "@ossy/router": "^1.39.1",
45
- "@ossy/sdk": "^1.39.1",
46
- "@ossy/tokens": "^1.12.1",
47
- "@ossy/users": "^1.12.1",
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": "b9708c9c3f368bb642fc3bb76912a68107db24b9"
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
- componentEntries: manifest.components,
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)