@jay-framework/stack-route-scanner 0.21.0 → 0.22.1

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/dist/index.d.mts CHANGED
@@ -18,7 +18,7 @@ type JayRoute = {
18
18
  /** NPM package name for plugin routes. Used by build to generate portable module paths. */
19
19
  packageName?: string;
20
20
  /**
21
- * Explicit params declared via <script type="application/jay-params"> in the jay-html.
21
+ * Explicit params declared via YAML body in headless component script tags (DL#156).
22
22
  * Used by static override routes to provide param values.
23
23
  * e.g., /products/ceramic-flower-vase declares { slug: 'ceramic-flower-vase' }.
24
24
  */
package/dist/index.mjs CHANGED
@@ -41,9 +41,27 @@ async function scanDirectory(BASE_DIR, directory, options) {
41
41
  routes = [...routes, ...await scanDirectory(BASE_DIR, fullPath, options)];
42
42
  } else if (item.name === options.jayHtmlFilename) {
43
43
  const route = convertToRoutePath(BASE_DIR, fullPath, options);
44
- const { params, validations } = await parseJayParams(fullPath);
44
+ const { params, validations } = await parseHeadlessProps(fullPath);
45
45
  if (params) {
46
- route.inferredParams = params;
46
+ const hasDynamicSegments = route.segments.some((s) => typeof s !== "string");
47
+ if (hasDynamicSegments) {
48
+ const dynamicParamNames = new Set(
49
+ route.segments.filter(
50
+ (s) => typeof s !== "string"
51
+ ).map((s) => s.name)
52
+ );
53
+ const routeParams = {};
54
+ for (const [k, v] of Object.entries(params)) {
55
+ if (dynamicParamNames.has(k)) {
56
+ routeParams[k] = v;
57
+ }
58
+ }
59
+ if (Object.keys(routeParams).length > 0) {
60
+ route.inferredParams = routeParams;
61
+ }
62
+ } else {
63
+ route.inferredParams = params;
64
+ }
47
65
  }
48
66
  if (validations.length > 0) {
49
67
  for (const v of validations) {
@@ -98,7 +116,7 @@ function dedentYaml(text) {
98
116
  const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
99
117
  return lines.map((l) => l.slice(minIndent)).join("\n");
100
118
  }
101
- async function parseJayParams(jayHtmlPath) {
119
+ async function parseHeadlessProps(jayHtmlPath) {
102
120
  let content;
103
121
  try {
104
122
  content = await promises.readFile(jayHtmlPath, "utf-8");
@@ -110,28 +128,37 @@ async function parseJayParams(jayHtmlPath) {
110
128
  blockTextElements: { script: true, style: true }
111
129
  });
112
130
  const head = root.querySelector("head");
131
+ const validations = [];
113
132
  const paramScripts = (head ?? root).querySelectorAll('script[type="application/jay-params"]');
114
- if (paramScripts.length === 0)
115
- return { params: void 0, validations: [] };
116
- if (paramScripts.length > 1) {
117
- return {
118
- params: void 0,
119
- validations: [
120
- 'Multiple <script type="application/jay-params"> tags found — expected at most one'
121
- ]
122
- };
133
+ if (paramScripts.length > 0) {
134
+ validations.push(
135
+ '<script type="application/jay-params"> is deprecated. Move the values into the YAML body of the headless component that uses them. See agent-kit/developer/routing.md for details.'
136
+ );
123
137
  }
124
- const body = dedentYaml(paramScripts[0].textContent ?? "");
125
- if (!body)
126
- return { params: void 0, validations: [] };
127
- try {
128
- return { params: YAML.parse(body), validations: [] };
129
- } catch (e) {
130
- return {
131
- params: void 0,
132
- validations: [`Failed to parse jay-params YAML: ${e.message}`]
133
- };
138
+ const headlessScripts = (head ?? root).querySelectorAll(
139
+ 'script[type="application/jay-headless"]'
140
+ );
141
+ let merged;
142
+ for (const script of headlessScripts) {
143
+ const body = dedentYaml(script.textContent ?? "");
144
+ if (!body)
145
+ continue;
146
+ try {
147
+ const parsed = YAML.parse(body);
148
+ if (parsed && typeof parsed === "object") {
149
+ if (!merged)
150
+ merged = {};
151
+ for (const [k, v] of Object.entries(parsed)) {
152
+ merged[k] = String(v);
153
+ }
154
+ }
155
+ } catch (e) {
156
+ validations.push(
157
+ `Failed to parse headless component props YAML: ${e.message}`
158
+ );
159
+ }
134
160
  }
161
+ return { params: merged, validations };
135
162
  }
136
163
  function parseRouteSegments(routePath) {
137
164
  return routePath.split("/").filter((s) => s.length > 0).map((segment) => {
@@ -160,7 +187,7 @@ async function scanRoutes(baseDir, options) {
160
187
  const sortedRoutes = sortRoutesByPriority(routes);
161
188
  const routesWithParams = sortedRoutes.filter((r) => r.inferredParams);
162
189
  if (routesWithParams.length > 0) {
163
- getLogger().info("[route-scanner] Routes with explicit params (jay-params):");
190
+ getLogger().info("[route-scanner] Routes with explicit params (headless props):");
164
191
  for (const route of routesWithParams) {
165
192
  getLogger().info(` ${route.rawRoute} → ${JSON.stringify(route.inferredParams)}`);
166
193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/stack-route-scanner",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -9,7 +9,7 @@
9
9
  "readme.md"
10
10
  ],
11
11
  "dependencies": {
12
- "@jay-framework/logger": "^0.21.0",
12
+ "@jay-framework/logger": "^0.22.1",
13
13
  "node-html-parser": "^6.1.12",
14
14
  "yaml": "^2.3.4"
15
15
  },
@@ -25,7 +25,7 @@
25
25
  "test:watch": "vitest"
26
26
  },
27
27
  "devDependencies": {
28
- "@jay-framework/dev-environment": "^0.21.0",
28
+ "@jay-framework/dev-environment": "^0.22.1",
29
29
  "@types/node": "^20.11.5",
30
30
  "nodemon": "^3.0.3",
31
31
  "replace-in-file": "^7.1.0",