@ossy/platform 1.38.5 → 1.38.7

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.5",
3
+ "version": "1.38.7",
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.5",
42
- "@ossy/observability": "^1.7.5",
43
- "@ossy/policies": "^1.12.5",
44
- "@ossy/router": "^1.39.5",
45
- "@ossy/sdk": "^1.39.5",
46
- "@ossy/tokens": "^1.12.5",
47
- "@ossy/users": "^1.12.5",
41
+ "@ossy/event-store": "^1.7.7",
42
+ "@ossy/observability": "^1.7.7",
43
+ "@ossy/policies": "^1.12.7",
44
+ "@ossy/router": "^1.39.7",
45
+ "@ossy/sdk": "^1.39.7",
46
+ "@ossy/tokens": "^1.12.7",
47
+ "@ossy/users": "^1.12.7",
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": "97d02b0762f2c0461abb1e6753fc3aaad30ee93f"
65
+ "gitHead": "f65df0cd6da864c314c4f424112e05e8e2dec0ff"
66
66
  }
@@ -13,24 +13,24 @@ import { ComponentSlotsProvider, Slot } from '@ossy/design-system'
13
13
  * to override or fill these slots with layout-specific components — the inner
14
14
  * provider shallow-merges on top.
15
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)
16
+ * Slot regions rendered here (namespaced keys from app `export const slots`):
17
+ * shell:header — top bar across full width
18
+ * shell:sidebar — left column, full height
19
+ * shell:toolbar — below header, above content (contextual actions)
20
+ * shell:notifications — floating / bottom corner (toasts, badges)
21
+ * shell:system-messages — banner above content (maintenance, errors)
22
22
  *
23
23
  * @param {{ slots?: Record<string, import('react').ComponentType | null>, children: import('react').ReactNode }} props
24
24
  */
25
25
  export function PlatformShell({ slots = {}, children }) {
26
26
  return (
27
27
  <ComponentSlotsProvider slots={slots}>
28
- <Slot name="system-messages" />
29
- <Slot name="header" />
30
- <Slot name="sidebar" />
31
- <Slot name="toolbar" />
28
+ <Slot name="shell:system-messages" />
29
+ <Slot name="shell:header" />
30
+ <Slot name="shell:sidebar" />
31
+ <Slot name="shell:toolbar" />
32
32
  {children}
33
- <Slot name="notifications" />
33
+ <Slot name="shell:notifications" />
34
34
  </ComponentSlotsProvider>
35
35
  )
36
36
  }
package/src/runtime.js CHANGED
@@ -5,6 +5,7 @@ import cookieParser from 'cookie-parser'
5
5
  import morgan from 'morgan'
6
6
  import { Router as OssyRouter } from '@ossy/router'
7
7
  import { loadManifest, resolveEntryUrl } from './server.js'
8
+ import { buildManifestSummary } from '@ossy/app/manifest/build-manifest-summary'
8
9
  import { ProxyInternal } from './proxy-internal.js'
9
10
  import { loadSite, invalidateSite } from './site-loader.js'
10
11
  import { createLogger } from '@ossy/observability'
@@ -48,7 +49,7 @@ function buildSiteContext (manifest, buildDir) {
48
49
  return promise
49
50
  }
50
51
 
51
- return { pageRouter, apiRouter, manifest, loadEntry }
52
+ return { pageRouter, apiRouter, manifest, manifestSummary: buildManifestSummary(manifest), loadEntry }
52
53
  }
53
54
 
54
55
  async function getSiteContext (domain) {
@@ -127,7 +128,7 @@ export async function startRuntime ({ port } = {}) {
127
128
  return
128
129
  }
129
130
 
130
- const { pageRouter, apiRouter, manifest, loadEntry } = context
131
+ const { pageRouter, apiRouter, manifest, manifestSummary, loadEntry } = context
131
132
 
132
133
  try {
133
134
  const apiRoute = apiRouter.getPageByUrl(requestUrl)
@@ -172,9 +173,14 @@ export async function startRuntime ({ port } = {}) {
172
173
  theme: req.userAppSettings?.theme || cloneSerializable(config?.theme),
173
174
  themes: cloneSerializable(config?.themes),
174
175
  resourceTemplates: cloneSerializable(manifest.resourceTemplates),
176
+ manifestSummary: cloneSerializable(manifestSummary),
175
177
  url: requestUrl,
176
178
  isAuthenticated: !!req.isAuthenticated,
177
- pages: manifest.pages.map((page) => ({ id: page.id, path: page.path })),
179
+ pages: manifest.pages.map((page) => ({
180
+ id: page.id,
181
+ path: page.path,
182
+ ...(page.package ? { package: page.package } : {}),
183
+ })),
178
184
  pageId: pageRoute.id,
179
185
  }
180
186
  const html = await mod.render(props)
package/src/server.js CHANGED
@@ -10,6 +10,8 @@ import { SDK } from '@ossy/sdk'
10
10
  import { AggregateRebuild } from '@ossy/event-store'
11
11
  import { TaskService } from './tasks/task-service.js'
12
12
  import { ChangeStream } from './tasks/change-stream.js'
13
+ import { CONTENT_SLOT_NAME } from '@ossy/app/runtime/resolve-shell-slots'
14
+ import { buildManifestSummary } from '@ossy/app/manifest/build-manifest-summary'
13
15
  import { registerResourceTemplate } from './resources/resource-template.registry.js'
14
16
  import { IntegrationService } from './integration.service.js'
15
17
  import { ActionService } from './actions/action.service.js'
@@ -55,12 +57,12 @@ export function loadManifest (buildDir) {
55
57
  const apis = entries.filter((e) => e.type === 'api' && validRoutable(e))
56
58
  const tasks = entries.filter((e) => e.type === 'task')
57
59
  const components = Array.isArray(manifest.components) ? manifest.components : []
58
- const slots = Array.isArray(manifest.slots) ? manifest.slots : []
59
60
  const resourceTemplates = Array.isArray(manifest.resourceTemplates) ? manifest.resourceTemplates : []
60
61
  const aggregates = Array.isArray(manifest.aggregates) ? manifest.aggregates : []
61
62
  const integrations = Array.isArray(manifest.integrations) ? manifest.integrations : []
62
63
  const startups = Array.isArray(manifest.startups) ? manifest.startups : []
63
64
  const actions = Array.isArray(manifest.actions) ? manifest.actions : []
65
+ const emails = Array.isArray(manifest.emails) ? manifest.emails : []
64
66
  const layouts = Array.isArray(manifest.layouts) ? manifest.layouts : []
65
67
  for (const e of entries) { if ((e.type === 'page' || e.type === 'api') && !validRoutable(e)) {
66
68
  log.warn(`Skipping ${e.type} "${e.id}" — manifest entry has no path.`)
@@ -72,12 +74,12 @@ export function loadManifest (buildDir) {
72
74
  apis,
73
75
  tasks,
74
76
  components,
75
- slots,
76
77
  resourceTemplates,
77
78
  aggregates,
78
79
  integrations,
79
80
  startups,
80
81
  actions,
82
+ emails,
81
83
  layouts,
82
84
  config: manifest.config || {},
83
85
  }
@@ -108,6 +110,7 @@ export async function startServer (options = {}) {
108
110
  }
109
111
 
110
112
  const manifest = loadManifest(buildDir)
113
+ const manifestSummary = buildManifestSummary(manifest)
111
114
  const config = manifest.config
112
115
  const supportedLanguages = Array.isArray(config.supportedLanguages) ? config.supportedLanguages : []
113
116
  const defaultLanguage = config.defaultLanguage
@@ -201,25 +204,12 @@ export async function startServer (options = {}) {
201
204
  }
202
205
  }
203
206
 
204
- // Slot loading import each component that declares `export const slot` and
205
- // build a map of slot-name entry URL for the render pipeline. Components
206
- // whose chunk fails to import are silently skipped so a broken slot never
207
- // prevents the server from starting. The slot entries are appended to
208
- // `componentEntries` at render time, keyed by slot name, so that the App's
209
- // ComponentSlotsProvider makes them available to the entire React tree.
210
- // Layout-level ComponentSlotsProviders can merge on top to override slots
211
- // for their subtree (e.g. layout fills `header` with brand-specific chrome).
212
- /** @type {Array<{ id: string, entry: string }>} */
213
- const slotComponentEntries = []
214
- for (const slotEntry of manifest.slots ?? []) {
215
- try {
216
- // Verify the chunk is importable at startup so we fail fast on bad builds.
217
- await import(resolveEntryUrl(slotEntry.entry, buildDir))
218
- slotComponentEntries.push({ id: slotEntry.slot, entry: slotEntry.entry })
219
- } catch (err) {
220
- log.warn(`Failed to load slot component "${slotEntry.slot}"`, undefined, err)
221
- }
222
- }
207
+ // App-controlled shell slots: `export const slots` in `*.layout.jsx` is recorded
208
+ // on `manifest.layouts[0].slots` at build time and resolved at render in page-runtime.
209
+ const layoutSlots =
210
+ layoutEntry && typeof layoutEntry.slots === 'object' && !Array.isArray(layoutEntry.slots)
211
+ ? layoutEntry.slots
212
+ : {}
223
213
 
224
214
  // Register the SDK so all tasks receive it as `sdk`.
225
215
  // Priority: explicit options.sdk → SDK.of() from env vars → null (direct-DB fallback in tasks).
@@ -332,19 +322,25 @@ export async function startServer (options = {}) {
332
322
  }
333
323
  const Layout = appLayout?.component ?? null
334
324
  const layoutEntry = appLayout?.entry ?? null
325
+ // Page component → `shell:content` is resolved in page-runtime (SSR + hydrate).
335
326
  const props = {
336
327
  ...config,
337
328
  ...(req.userAppSettings || {}),
338
329
  theme: req.userAppSettings?.theme || cloneSerializable(config?.theme),
339
330
  themes: cloneSerializable(config?.themes),
340
331
  resourceTemplates: cloneSerializable(manifest.resourceTemplates),
332
+ manifestSummary: cloneSerializable(manifestSummary),
341
333
  url: requestUrl,
342
334
  isAuthenticated: !!req.isAuthenticated,
343
- pages: manifest.pages.map((page) => ({ id: page.id, path: page.path })),
335
+ pages: manifest.pages.map((page) => ({
336
+ id: page.id,
337
+ path: page.path,
338
+ ...(page.package ? { package: page.package } : {}),
339
+ })),
344
340
  pageId: pageRoute.id,
345
- // Slot component entries are prepended so that layout-level
346
- // ComponentSlotsProviders (which merge on top) can override them.
347
- componentEntries: [...slotComponentEntries, ...manifest.components],
341
+ componentEntries: manifest.components,
342
+ layoutSlots,
343
+ contentSlotName: CONTENT_SLOT_NAME,
348
344
  Layout,
349
345
  layoutEntry,
350
346
  }