@ossy/app 3.17.0 → 3.19.0

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/cli/build.task.js CHANGED
@@ -64,24 +64,24 @@ function ensureDir (dir) {
64
64
  /**
65
65
  * Copy heavy optional browser deps that feature packages load via non-literal
66
66
  * `import()` so Rollup never opens them (CI OOM). `@ossy/resumes` Word export
67
- * loads `/static/vendor/docx.mjs` in the browser; skip quietly when not installed.
67
+ * loads `/static/vendor/docx.mjs`. Skip quietly when not installed.
68
68
  *
69
69
  * @param {string} staticOutDir
70
70
  * @param {string} projectRoot
71
71
  */
72
- function copyExternalBrowserVendors (staticOutDir, projectRoot) {
73
- let docxEntry
72
+ async function copyExternalBrowserVendors (staticOutDir, projectRoot) {
73
+ const requireFromRoot = createRequire(path.join(projectRoot, 'package.json'))
74
+ const destDir = path.join(staticOutDir, 'vendor')
75
+ ensureDir(destDir)
76
+
74
77
  try {
75
- docxEntry = createRequire(path.join(projectRoot, 'package.json')).resolve('docx')
78
+ const docxEntry = requireFromRoot.resolve('docx')
79
+ // `docx` resolves to dist/index.cjs; ship the self-contained ESM build for browsers.
80
+ const src = path.join(path.dirname(docxEntry), 'index.mjs')
81
+ if (fs.existsSync(src)) fs.copyFileSync(src, path.join(destDir, 'docx.mjs'))
76
82
  } catch {
77
- return
83
+ // optional
78
84
  }
79
- // `docx` resolves to dist/index.cjs; ship the self-contained ESM build for browsers.
80
- const src = path.join(path.dirname(docxEntry), 'index.mjs')
81
- if (!fs.existsSync(src)) return
82
- const destDir = path.join(staticOutDir, 'vendor')
83
- ensureDir(destDir)
84
- fs.copyFileSync(src, path.join(destDir, 'docx.mjs'))
85
85
  }
86
86
 
87
87
  // One source of truth for the per-entry name. Used both as the Rollup
@@ -489,7 +489,7 @@ export async function build (cliArgs = []) {
489
489
 
490
490
  await bundle.close()
491
491
 
492
- copyExternalBrowserVendors(staticOutDir, cwd)
492
+ await copyExternalBrowserVendors(staticOutDir, cwd)
493
493
 
494
494
  if (fs.existsSync(publicSrc)) {
495
495
  fs.cpSync(publicSrc, publicOutDir, { recursive: true, force: true })
@@ -609,7 +609,28 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
609
609
  'Assign components to app slots in `*.layout.jsx` via `export const slots`.',
610
610
  )
611
611
  }
612
- components.push(withSourcePath({ id, entry: url, package: entryPackage(entryInfo, appPackageName) }, entryInfo, projectRoot))
612
+ /** @type {Record<string, unknown>} */
613
+ const componentEntry = {
614
+ id,
615
+ entry: url,
616
+ package: entryPackage(entryInfo, appPackageName),
617
+ }
618
+ const widget = rawMeta.widget
619
+ if (widget && typeof widget === 'object' && !Array.isArray(widget)) {
620
+ const title = typeof widget.title === 'string'
621
+ ? widget.title.trim()
622
+ : ''
623
+ const description = typeof widget.description === 'string'
624
+ ? widget.description.trim()
625
+ : ''
626
+ if (title) {
627
+ componentEntry.widget = {
628
+ title,
629
+ ...(description ? { description } : {}),
630
+ }
631
+ }
632
+ }
633
+ components.push(withSourcePath(componentEntry, entryInfo, projectRoot))
613
634
  continue
614
635
  }
615
636
 
@@ -780,6 +801,17 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
780
801
  }
781
802
  }
782
803
 
804
+ const widgets = components
805
+ .filter((component) => component?.widget?.title)
806
+ .map((component) => ({
807
+ componentId: component.id,
808
+ title: component.widget.title,
809
+ ...(component.widget.description
810
+ ? { description: component.widget.description }
811
+ : {}),
812
+ ...(component.package ? { package: component.package } : {}),
813
+ }))
814
+
783
815
  const manifest = {
784
816
  entries,
785
817
  components,
@@ -798,6 +830,7 @@ export function manifestPlugin ({ entriesByStub, srcDir, staticOutDir, configVal
798
830
  actionRoutes,
799
831
  taskCatalog,
800
832
  taskGraphEdges,
833
+ widgets,
801
834
  }
802
835
  fs.mkdirSync(path.dirname(manifestPath), { recursive: true })
803
836
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/app",
3
- "version": "3.17.0",
3
+ "version": "3.19.0",
4
4
  "description": "",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -50,21 +50,21 @@
50
50
  "@babel/preset-react": "^7.26.3",
51
51
  "@babel/register": "^7.25.9",
52
52
  "@ossy/authentication": "^3.11.1",
53
- "@ossy/design-system": "^3.17.0",
53
+ "@ossy/design-system": "^3.19.0",
54
54
  "@ossy/locale": "^3.4.0",
55
- "@ossy/manifest": "^3.17.0",
56
- "@ossy/package-catalog": "^3.17.0",
55
+ "@ossy/manifest": "^3.18.0",
56
+ "@ossy/package-catalog": "^3.19.0",
57
57
  "@ossy/pages": "^3.0.9",
58
- "@ossy/platform": "^3.17.0",
59
- "@ossy/resources": "^3.17.0",
58
+ "@ossy/platform": "^3.19.0",
59
+ "@ossy/resources": "^3.19.0",
60
60
  "@ossy/router": "^3.0.9",
61
- "@ossy/router-react": "^3.17.0",
62
- "@ossy/schema": "^3.17.0",
63
- "@ossy/sdk": "^3.17.0",
61
+ "@ossy/router-react": "^3.18.0",
62
+ "@ossy/schema": "^3.18.0",
63
+ "@ossy/sdk": "^3.18.0",
64
64
  "@ossy/sdk-react": "^3.12.0",
65
- "@ossy/themes": "^3.8.0",
66
- "@ossy/users": "^3.17.0",
67
- "@ossy/workspaces": "^3.17.0",
65
+ "@ossy/themes": "^3.18.0",
66
+ "@ossy/users": "^3.18.0",
67
+ "@ossy/workspaces": "^3.18.0",
68
68
  "@rollup/plugin-alias": "^6.0.0",
69
69
  "@rollup/plugin-babel": "^7.1.0",
70
70
  "@rollup/plugin-commonjs": "^29.0.0",
@@ -98,5 +98,5 @@
98
98
  "README.md",
99
99
  "tsconfig.json"
100
100
  ],
101
- "gitHead": "91ff7dffe9b0bddcb823daee67213d2008eb64e9"
101
+ "gitHead": "0ac103001a468f56597c02cf1c6802bf7bd89b33"
102
102
  }
@@ -46,6 +46,11 @@ export function defaultAppSettings() {
46
46
  /** Task catalog from `app build` (ADR 0011) — for automation topology UI. */
47
47
  taskCatalog: undefined,
48
48
  taskGraphEdges: undefined,
49
+ /**
50
+ * Dashboard pin catalog from `app build` — components that export
51
+ * `metadata.widget` (any package can register).
52
+ */
53
+ widgets: undefined,
49
54
  /** Action id → HTTP route map for sdk.invoke transport routing. */
50
55
  actionRoutes: undefined,
51
56
  /**
@@ -17,6 +17,10 @@ function buildPatch (workspace, workspaces) {
17
17
  * Persists workspace shell snapshot to the signed user-app-settings cookie
18
18
  * (via ProxyInternal PATCH /@ossy/users/me/app-settings) so SSR bootstrap
19
19
  * props stay stable across full-page navigations.
20
+ *
21
+ * Also keeps `sdk.workspaceId` in sync so workspace-scoped action invocations
22
+ * include the workspaceId header even when it was absent from the initial SSR
23
+ * bootstrap props.
20
24
  */
21
25
  export function WorkspaceAppSettingsSync () {
22
26
  const sdk = useSdk()
@@ -24,6 +28,16 @@ export function WorkspaceAppSettingsSync () {
24
28
  const { data: workspaces } = sdk.read(ListWorkspaces)
25
29
  const lastPatchRef = useRef('')
26
30
 
31
+ // Keep sdk.workspaceId in sync with the live workspace read so that
32
+ // sdk.invoke sends the workspaceId header for workspace-scoped actions.
33
+ // useSdk() returns a wrapper object; the underlying ReactSdk is at .sdk.
34
+ useEffect(() => {
35
+ const reactSdk = sdk.sdk
36
+ if (workspace?.id && reactSdk?.workspaceId !== workspace.id) {
37
+ reactSdk?.updateConfig({ workspaceId: workspace.id })
38
+ }
39
+ }, [workspace?.id, sdk])
40
+
27
41
  useEffect(() => {
28
42
  const patch = buildPatch(workspace, workspaces)
29
43
  if (!patch) return