@dfosco/storyboard-core 1.6.0 → 1.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfosco/storyboard-core",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/index.js CHANGED
@@ -32,7 +32,7 @@ export { mountDevTools } from './devtools.js'
32
32
  export { mountSceneDebug } from './sceneDebug.js'
33
33
 
34
34
  // Viewfinder utilities
35
- export { hash, resolveSceneRoute } from './viewfinder.js'
35
+ export { hash, resolveSceneRoute, getSceneMeta } from './viewfinder.js'
36
36
 
37
37
  // Comments system
38
38
  export { initCommentsConfig, getCommentsConfig, isCommentsEnabled } from './comments/config.js'
package/src/viewfinder.js CHANGED
@@ -17,7 +17,7 @@ export function hash(str) {
17
17
  * Resolve the target route path for a scene.
18
18
  *
19
19
  * 1. If scene name matches a known route (case-insensitive), use that route
20
- * 2. If scene data has a `route` key, use that
20
+ * 2. If scene data has a `sceneMeta.route` or `route` key, use that
21
21
  * 3. Fall back to root "/"
22
22
  *
23
23
  * @param {string} sceneName
@@ -32,12 +32,13 @@ export function resolveSceneRoute(sceneName, knownRoutes = []) {
32
32
  }
33
33
  }
34
34
 
35
- // Check for explicit `route` key in scene data
35
+ // Check for explicit route in sceneMeta or top-level route key
36
36
  try {
37
37
  const data = loadScene(sceneName)
38
- if (data?.route) {
39
- const route = data.route.startsWith('/') ? data.route : `/${data.route}`
40
- return `${route}?scene=${encodeURIComponent(sceneName)}`
38
+ const route = data?.sceneMeta?.route || data?.route
39
+ if (route) {
40
+ const normalized = route.startsWith('/') ? route : `/${route}`
41
+ return `${normalized}?scene=${encodeURIComponent(sceneName)}`
41
42
  }
42
43
  } catch {
43
44
  // ignore load errors
@@ -45,3 +46,18 @@ export function resolveSceneRoute(sceneName, knownRoutes = []) {
45
46
 
46
47
  return `/?scene=${encodeURIComponent(sceneName)}`
47
48
  }
49
+
50
+ /**
51
+ * Get sceneMeta for a scene (route, author, etc).
52
+ *
53
+ * @param {string} sceneName
54
+ * @returns {{ route?: string, author?: string } | null}
55
+ */
56
+ export function getSceneMeta(sceneName) {
57
+ try {
58
+ const data = loadScene(sceneName)
59
+ return data?.sceneMeta || null
60
+ } catch {
61
+ return null
62
+ }
63
+ }
@@ -1,5 +1,5 @@
1
1
  import { init } from './loader.js'
2
- import { hash, resolveSceneRoute } from './viewfinder.js'
2
+ import { hash, resolveSceneRoute, getSceneMeta } from './viewfinder.js'
3
3
 
4
4
  const makeIndex = () => ({
5
5
  scenes: {
@@ -8,6 +8,9 @@ const makeIndex = () => ({
8
8
  'custom-route': { route: 'Overview', title: 'Custom' },
9
9
  'absolute-route': { route: '/Forms', title: 'Absolute' },
10
10
  'no-route': { title: 'No route key' },
11
+ 'meta-route': { sceneMeta: { route: 'Repositories' }, title: 'Meta Route' },
12
+ 'meta-author': { sceneMeta: { author: 'dfosco' }, title: 'With Author' },
13
+ 'meta-both': { sceneMeta: { route: '/Overview', author: 'octocat' }, title: 'Both' },
11
14
  },
12
15
  objects: {},
13
16
  records: {},
@@ -84,4 +87,39 @@ describe('resolveSceneRoute', () => {
84
87
  })
85
88
  expect(resolveSceneRoute('has spaces', [])).toBe('/?scene=has%20spaces')
86
89
  })
90
+
91
+ it('uses sceneMeta.route when no route matches', () => {
92
+ expect(resolveSceneRoute('meta-route', routes)).toBe('/Repositories?scene=meta-route')
93
+ })
94
+
95
+ it('uses sceneMeta.route with absolute path', () => {
96
+ expect(resolveSceneRoute('meta-both', routes)).toBe('/Overview?scene=meta-both')
97
+ })
98
+
99
+ it('prefers sceneMeta.route over top-level route key', () => {
100
+ init({
101
+ scenes: { conflict: { route: 'Forms', sceneMeta: { route: 'Dashboard' } } },
102
+ objects: {},
103
+ records: {},
104
+ })
105
+ expect(resolveSceneRoute('conflict', [])).toBe('/Dashboard?scene=conflict')
106
+ })
107
+ })
108
+
109
+ describe('getSceneMeta', () => {
110
+ it('returns sceneMeta when present', () => {
111
+ expect(getSceneMeta('meta-author')).toEqual({ author: 'dfosco' })
112
+ })
113
+
114
+ it('returns sceneMeta with both fields', () => {
115
+ expect(getSceneMeta('meta-both')).toEqual({ route: '/Overview', author: 'octocat' })
116
+ })
117
+
118
+ it('returns null when no sceneMeta', () => {
119
+ expect(getSceneMeta('default')).toBeNull()
120
+ })
121
+
122
+ it('returns null for nonexistent scene', () => {
123
+ expect(getSceneMeta('nonexistent')).toBeNull()
124
+ })
87
125
  })