@jxsuite/compiler 0.28.5 → 0.30.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/compiler",
3
- "version": "0.28.5",
3
+ "version": "0.30.1",
4
4
  "description": "Jx static HTML compiler, island detector, and site builder",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,20 +35,21 @@
35
35
  "scripts": {
36
36
  "prepare": "bun build src/compiler.ts src/cli.ts --outdir=dist --format=esm --minify --target=node",
37
37
  "build": "bun run prepare",
38
+ "build:metafile": "bun build src/compiler.ts src/cli.ts --outdir=dist --format=esm --minify --target=node --metafile=metafile.json",
38
39
  "build:site": "bun src/cli.ts build",
39
40
  "test": "bun test --isolate",
40
41
  "test:coverage": "bun test --isolate --coverage",
41
42
  "upgrade": "bunx npm-check-updates -u && bun install"
42
43
  },
43
44
  "dependencies": {
44
- "@apidevtools/json-schema-ref-parser": "^15.3.6",
45
- "@jxsuite/parser": "^0.28.5",
46
- "@jxsuite/runtime": "^0.28.5",
47
- "@jxsuite/schema": "^0.28.5",
48
- "sharp": "^0.35.1"
45
+ "@apidevtools/json-schema-ref-parser": "^15.4.0",
46
+ "@jxsuite/parser": "^0.30.1",
47
+ "@jxsuite/runtime": "^0.30.1",
48
+ "@jxsuite/schema": "^0.30.1",
49
+ "sharp": "^0.35.2"
49
50
  },
50
51
  "devDependencies": {
51
- "@happy-dom/global-registrator": "^20.10.2",
52
+ "@happy-dom/global-registrator": "^20.10.6",
52
53
  "@types/mdast": "^4.0.4"
53
54
  }
54
55
  }
package/src/cli.ts CHANGED
@@ -9,11 +9,16 @@
9
9
 
10
10
  import { resolve } from "node:path";
11
11
 
12
- const args = process.argv.slice(2);
13
- const [command] = args;
12
+ // The CLI body runs inside an async function rather than via top-level await: when a test pulls this
13
+ // Entry in with a dynamic import(), Bun's test runtime drops the continuation after a top-level await
14
+ // (it never resumes on Windows), so the build step would silently never run. `ready` lets the
15
+ // In-process CLI harness await the same sequence.
16
+ async function main() {
17
+ const args = process.argv.slice(2);
18
+ const [command] = args;
14
19
 
15
- if (!command || command === "--help" || command === "-h") {
16
- console.log(`Usage: jx <command> [options]
20
+ if (!command || command === "--help" || command === "-h") {
21
+ console.log(`Usage: jx <command> [options]
17
22
 
18
23
  Commands:
19
24
  build [root] Build a Jx site to dist/
@@ -21,39 +26,43 @@ Commands:
21
26
  Options:
22
27
  --verbose Print detailed build progress
23
28
  --no-clean Don't clean outDir before building`);
24
- process.exit(0);
25
- }
29
+ process.exit(0);
30
+ }
26
31
 
27
- if (command === "build") {
28
- const rest = args.slice(1);
29
- const flags = new Set(rest.filter((a) => a.startsWith("--")));
30
- const positional = rest.find((a) => !a.startsWith("--"));
32
+ if (command === "build") {
33
+ const rest = args.slice(1);
34
+ const flags = new Set(rest.filter((a) => a.startsWith("--")));
35
+ const positional = rest.find((a) => !a.startsWith("--"));
31
36
 
32
- const projectRoot = resolve(positional ?? ".");
33
- const verbose = flags.has("--verbose");
34
- const clean = !flags.has("--no-clean");
37
+ const projectRoot = resolve(positional ?? ".");
38
+ const verbose = flags.has("--verbose");
39
+ const clean = !flags.has("--no-clean");
35
40
 
36
- console.log(`Building site from ${projectRoot}...`);
41
+ console.log(`Building site from ${projectRoot}...`);
37
42
 
38
- try {
39
- const { buildSite } = await import("./site/site-build.ts");
40
- const result = await buildSite(projectRoot, { clean, verbose });
43
+ try {
44
+ const { buildSite } = await import("./site/site-build.ts");
45
+ const result = await buildSite(projectRoot, { clean, verbose });
41
46
 
42
- if (result.errors.length > 0) {
43
- console.error(`\nBuild completed with ${result.errors.length} error(s):`);
44
- for (const err of result.errors) {
45
- console.error(` - ${err}`);
47
+ if (result.errors.length > 0) {
48
+ console.error(`\nBuild completed with ${result.errors.length} error(s):`);
49
+ for (const err of result.errors) {
50
+ console.error(` - ${err}`);
51
+ }
52
+ process.exit(1);
46
53
  }
54
+
55
+ console.log(`\nDone: ${result.routes} routes → ${result.files} files`);
56
+ } catch (error) {
57
+ const err = error as Error;
58
+ console.error(`Build failed: ${err.message}`);
47
59
  process.exit(1);
48
60
  }
49
-
50
- console.log(`\nDone: ${result.routes} routes → ${result.files} files`);
51
- } catch (error) {
52
- const err = error as Error;
53
- console.error(`Build failed: ${err.message}`);
61
+ } else {
62
+ console.error(`Unknown command: ${command}\nRun "jx --help" for usage.`);
54
63
  process.exit(1);
55
64
  }
56
- } else {
57
- console.error(`Unknown command: ${command}\nRun "jx --help" for usage.`);
58
- process.exit(1);
59
65
  }
66
+
67
+ // oxlint-disable-next-line unicorn/prefer-top-level-await
68
+ export const ready = main();
@@ -1,12 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import { runCli } from "./compiler.ts";
3
3
 
4
- const [src, out] = process.argv.slice(2);
5
- if (src) {
6
- try {
7
- await runCli(src, out);
8
- } catch (error) {
9
- console.error(error);
10
- process.exit(1);
4
+ // Body wrapped in an async function rather than a top-level await: Bun's test runtime drops the
5
+ // Continuation after a dynamically-imported module's top-level await (it never resumes on Windows),
6
+ // Which would skip the error handling. `ready` lets the in-process CLI harness await the sequence.
7
+ async function main() {
8
+ const [src, out] = process.argv.slice(2);
9
+ if (src) {
10
+ try {
11
+ await runCli(src, out);
12
+ } catch (error) {
13
+ console.error(error);
14
+ process.exit(1);
15
+ }
11
16
  }
12
17
  }
18
+
19
+ // oxlint-disable-next-line unicorn/prefer-top-level-await
20
+ export const ready = main();
package/src/compiler.ts CHANGED
@@ -22,13 +22,9 @@ import {
22
22
  isDynamic,
23
23
  tagNameToClassName,
24
24
  } from "./shared.ts";
25
- import { compileServer, compileSiteServer } from "./targets/compile-server.ts";
25
+ import { compileServer } from "./targets/compile-server.ts";
26
26
 
27
- import {
28
- compileElement,
29
- compileElementPage,
30
- emitElementModule,
31
- } from "./targets/compile-element.ts";
27
+ import { emitElementModule } from "./targets/compile-element.ts";
32
28
  import { compileStaticPage } from "./targets/compile-static.ts";
33
29
  import { compileClient } from "./targets/compile-client.ts";
34
30
  import { isClassDef } from "@jxsuite/schema/guards";
@@ -37,14 +33,10 @@ import type { JxDocument, JxStyle } from "@jxsuite/schema/types";
37
33
  import type { FormatRegistry } from "@jxsuite/schema/format-registry";
38
34
 
39
35
  // Re-exports for consumers
40
- export {
41
- isDynamic,
42
- compileServer,
43
- compileSiteServer,
44
- compileElement,
45
- compileElementPage,
46
- compileClient,
47
- };
36
+ export { isDynamic } from "./shared.ts";
37
+ export { compileServer, compileSiteServer } from "./targets/compile-server.ts";
38
+ export { compileElement, compileElementPage } from "./targets/compile-element.ts";
39
+ export { compileClient } from "./targets/compile-client.ts";
48
40
 
49
41
  // ─── Entry ────────────────────────────────────────────────────────────────────
50
42
 
package/src/shared.ts CHANGED
@@ -7,10 +7,12 @@
7
7
  */
8
8
 
9
9
  import { RESERVED_KEYS, camelToKebab, toCSSText } from "@jxsuite/runtime";
10
- import { compileExpression, evaluateExpression, isMutating } from "@jxsuite/runtime/expression";
10
+ import { evaluateExpression, isMutating } from "@jxsuite/runtime/expression";
11
11
  import {
12
+ childrenContainArray,
12
13
  isExpressionDef,
13
14
  isFunctionDef,
15
+ isMappedArray,
14
16
  isPrototypeDef,
15
17
  isRef,
16
18
  isSchemaOnlyDef as isSchemaOnly,
@@ -22,7 +24,6 @@ import type { ExpressionNode } from "@jxsuite/runtime/expression";
22
24
  import type {
23
25
  JsonValue,
24
26
  JxElement,
25
- JxMappedArray,
26
27
  JxMutableNode,
27
28
  JxPrototypeDef,
28
29
  JxRef,
@@ -32,15 +33,9 @@ import type {
32
33
  } from "@jxsuite/schema/types";
33
34
 
34
35
  // Re-export runtime utilities used by submodules
35
- export {
36
- camelToKebab,
37
- toCSSText,
38
- RESERVED_KEYS,
39
- compileExpression,
40
- isMutating,
41
- evaluateExpression,
42
- };
43
- export type { ExpressionNode };
36
+ export { RESERVED_KEYS, camelToKebab, toCSSText } from "@jxsuite/runtime";
37
+ export { compileExpression, evaluateExpression, isMutating } from "@jxsuite/runtime/expression";
38
+ export type { ExpressionNode } from "@jxsuite/runtime/expression";
44
39
 
45
40
  // CDN defaults
46
41
  export const DEFAULT_REACTIVITY_SRC = "https://esm.sh/@vue/reactivity@3.5.32";
@@ -110,10 +105,7 @@ export function isDynamic(def: JxElement | JxMutableNode | string) {
110
105
  if (def.$switch) {
111
106
  return true;
112
107
  }
113
- if (
114
- !Array.isArray(def.children) &&
115
- (def.children as JxMappedArray | undefined)?.$prototype === "Array"
116
- ) {
108
+ if (isMappedArray(def) || childrenContainArray(def.children)) {
117
109
  return true;
118
110
  }
119
111
 
@@ -170,10 +162,7 @@ export function isNodeDynamic(def: JxElement | JxMutableNode | string) {
170
162
  if (def.$switch) {
171
163
  return true;
172
164
  }
173
- if (
174
- !Array.isArray(def.children) &&
175
- (def.children as JxMappedArray | undefined)?.$prototype === "Array"
176
- ) {
165
+ if (isMappedArray(def) || childrenContainArray(def.children)) {
177
166
  return true;
178
167
  }
179
168
 
@@ -252,9 +241,9 @@ export function createCompileContext(
252
241
  scopeDefs: Record<string, unknown> = {},
253
242
  media: Record<string, string> = {},
254
243
  ): CompileContext {
255
- const scope = raw?.state
244
+ const scope: Record<string, unknown> = raw?.state
256
245
  ? buildInitialScope(raw.state, parentScope)
257
- : (parentScope ?? Object.create(null));
246
+ : (parentScope ?? (Object.create(null) as Record<string, unknown>));
258
247
  return { media, scope, scopeDefs };
259
248
  }
260
249
 
@@ -267,7 +256,7 @@ export function buildInitialScope(
267
256
  defs: Record<string, JxStateDefinition> = {},
268
257
  parentScope: Record<string, unknown> | null = null,
269
258
  ) {
270
- const scope = Object.create(parentScope ?? null);
259
+ const scope = Object.create(parentScope ?? null) as Record<string, unknown>;
271
260
 
272
261
  for (const [key, def] of Object.entries(defs)) {
273
262
  if (typeof def !== "object" || def === null || Array.isArray(def)) {
@@ -306,7 +295,9 @@ export function buildInitialScope(
306
295
  if (isFunctionDef(def)) {
307
296
  if (def.body) {
308
297
  const names = def.parameters ? paramNames(def.parameters) : (def.arguments ?? []);
309
- const fn = new Function("state", ...names, def.body);
298
+ const fn = new Function("state", ...names, def.body) as (
299
+ state: Record<string, unknown>,
300
+ ) => unknown;
310
301
  if (def.body.includes("return")) {
311
302
  defineLazyScopeValue(scope, key, () => fn(scope));
312
303
  } else {
@@ -393,7 +384,7 @@ export function resolveRefValue(refValue: unknown, scope: Record<string, unknown
393
384
  }
394
385
  if (refValue.startsWith("$map/")) {
395
386
  const parts = refValue.split("/");
396
- const [, key] = parts;
387
+ const key = parts[1]!;
397
388
  const base = (scope.$map as Record<string, unknown> | undefined)?.[key] ?? scope[`$map/${key}`];
398
389
  return parts.length > 2 ? getPathValue(base, parts.slice(2).join("/")) : base;
399
390
  }
@@ -417,10 +408,16 @@ export function evaluateStaticTemplate(str: string, scope: Record<string, unknow
417
408
  try {
418
409
  const singleExprMatch = str.match(/^\$\{(.+)\}$/s);
419
410
  if (singleExprMatch) {
420
- const fn = new Function("state", "$map", `return (${singleExprMatch[1]})`);
411
+ const fn = new Function("state", "$map", `return (${singleExprMatch[1]})`) as (
412
+ state: Record<string, unknown>,
413
+ $map: unknown,
414
+ ) => unknown;
421
415
  return fn(scope, scope?.$map);
422
416
  }
423
- const fn = new Function("state", "$map", `return \`${str}\``);
417
+ const fn = new Function("state", "$map", `return \`${str}\``) as (
418
+ state: Record<string, unknown>,
419
+ $map: unknown,
420
+ ) => unknown;
424
421
  return fn(scope, scope?.$map);
425
422
  } catch {
426
423
  return null;
@@ -632,7 +629,7 @@ export function compileStyles(
632
629
  if (mProps) {
633
630
  rules.push(`${atRule} { ${selector} { ${mProps} } }`);
634
631
  }
635
- for (const [sel, sub] of Object.entries(/** @type {Record<string, unknown>} */ val)) {
632
+ for (const [sel, sub] of Object.entries(val as Record<string, unknown>)) {
636
633
  if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
637
634
  continue;
638
635
  }
@@ -753,7 +750,7 @@ function emitNestedElement(
753
750
  if (mProps) {
754
751
  rules.push(`${atRule} { ${selector} { ${mProps} } }`);
755
752
  }
756
- for (const [sel, sub] of Object.entries(/** @type {Record<string, unknown>} */ val)) {
753
+ for (const [sel, sub] of Object.entries(val as Record<string, unknown>)) {
757
754
  if (sub === null || typeof sub !== "object" || Array.isArray(sub)) {
758
755
  continue;
759
756
  }
@@ -995,8 +992,7 @@ function _walkSrc(def: JxElement | JxMutableNode | string, srcs: Set<string>) {
995
992
  * @returns {{ key: string; exportName: string; src: string }[]}
996
993
  */
997
994
  export function collectServerEntries(doc: JxElement) {
998
- /** @type {Map<string, { key: string; exportName: string; src: string }>} */
999
- const entries = new Map();
995
+ const entries = new Map<string, { key: string; exportName: string; src: string }>();
1000
996
  _walkServerEntries(doc, entries);
1001
997
  return [...entries.values()];
1002
998
  }
@@ -1028,7 +1024,7 @@ function _walkServerEntries(
1028
1024
 
1029
1025
  // ─── Component pre-rendering ─────────────────────────────────────────────────
1030
1026
 
1031
- const SELF_CLOSING = new Set<string>([
1027
+ export const SELF_CLOSING = new Set<string>([
1032
1028
  "input",
1033
1029
  "br",
1034
1030
  "hr",
@@ -21,6 +21,7 @@ import type { FormatEntry, FormatRegistry } from "@jxsuite/schema/format-registr
21
21
  import type {
22
22
  ContentTypeDef,
23
23
  ContentTypeSchema,
24
+ ContentTypeSchemaField,
24
25
  JxMutableNode,
25
26
  ProjectConfig,
26
27
  } from "@jxsuite/schema/types";
@@ -36,16 +37,16 @@ import type { ContentLoaderEntry } from "@jxsuite/parser/types";
36
37
  * @returns {ContentLoaderEntry[]} Array of ContentEntry shapes
37
38
  */
38
39
  function loadJSONEntries(filePath: string) {
39
- const raw = JSON.parse(readFileSync(filePath, "utf8"));
40
+ const raw = JSON.parse(readFileSync(filePath, "utf8")) as unknown;
40
41
  if (Array.isArray(raw)) {
41
- return raw.map((item: Record<string, unknown>, i: number) => ({
42
+ return (raw as Record<string, unknown>[]).map((item: Record<string, unknown>, i: number) => ({
42
43
  body: null,
43
44
  data: item,
44
45
  id: (item.id as string) ?? `${basename(filePath, ".json")}-${i}`,
45
46
  }));
46
47
  }
47
48
  // Single object file — filename is the id
48
- const rawObj: Record<string, unknown> = raw;
49
+ const rawObj = raw as Record<string, unknown>;
49
50
  return [
50
51
  {
51
52
  body: null,
@@ -108,7 +109,7 @@ export async function loadContentTypes(
108
109
  ) {
109
110
  const result = loadContentConfig(projectRoot, projectConfig);
110
111
  if (!result) {
111
- return new Map();
112
+ return new Map<string, ContentLoaderEntry[]>();
112
113
  }
113
114
 
114
115
  const { config } = result;
@@ -359,7 +360,8 @@ export function resolveContentTypeRefs(
359
360
  continue;
360
361
  }
361
362
 
362
- for (const [field, def] of Object.entries(schema.properties)) {
363
+ for (const [field, rawDef] of Object.entries(schema.properties)) {
364
+ const def = rawDef as ContentTypeSchemaField;
363
365
  if (!def.$ref?.startsWith("#/contentTypes/")) {
364
366
  continue;
365
367
  }
@@ -36,7 +36,7 @@ export function injectContext(
36
36
  doc: JxDocument,
37
37
  projectConfig: ProjectConfig,
38
38
  route: SiteRoute,
39
- contentTypes = new Map<string, ContentLoaderEntry[]>(),
39
+ _contentTypes = new Map<string, ContentLoaderEntry[]>(),
40
40
  projectRoot: string | null = null,
41
41
  ) {
42
42
  if (!doc.state) {
@@ -94,13 +94,13 @@ export function cacheKey(srcPath: string, config: ImageConfig) {
94
94
  * @param {string} projectRoot
95
95
  * @returns {CacheManifest}
96
96
  */
97
- export function loadCache(projectRoot: string) {
97
+ export function loadCache(projectRoot: string): CacheManifest {
98
98
  const manifestPath = resolve(getImageCacheDir(projectRoot), "manifest.json");
99
99
  if (!existsSync(manifestPath)) {
100
100
  return { entries: {}, version: 1 };
101
101
  }
102
102
  try {
103
- return JSON.parse(readFileSync(manifestPath, "utf8"));
103
+ return JSON.parse(readFileSync(manifestPath, "utf8")) as CacheManifest;
104
104
  } catch {
105
105
  return { entries: {}, version: 1 };
106
106
  }
@@ -10,7 +10,8 @@ import { errorMessage } from "@jxsuite/schema/parse";
10
10
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
11
11
  import { basename, extname, resolve } from "node:path";
12
12
  import { createRequire } from "node:module";
13
- import type { FormatEnum } from "sharp";
13
+ // Sharp 0.35 only has a default export (function merged with a types namespace).
14
+ import type SharpNS from "sharp";
14
15
 
15
16
  export interface ImageVariant {
16
17
  width: number; // Pixel width of the variant
@@ -41,8 +42,14 @@ export interface ImageConfig {
41
42
  remoteDomains?: string[];
42
43
  }
43
44
 
44
- // oxlint-disable-next-line typescript/consistent-type-imports -- sharp is an optional native dep loaded lazily; its `export =` module type is only nameable via import()
45
- type SharpModule = typeof import("sharp");
45
+ type SharpModule = typeof SharpNS;
46
+ /**
47
+ * Accepted output-format type, derived from the Sharp instance's own toFormat signature. Reaching
48
+ * into the `SharpNS.FormatEnum` namespace instead is fragile: sharp ships as `export = sharp`, so
49
+ * whether a default type-import exposes the merged namespace depends on esModuleInterop resolution
50
+ * (differs local vs CI).
51
+ */
52
+ type SharpFormat = Parameters<ReturnType<SharpModule>["toFormat"]>[0];
46
53
 
47
54
  let _sharp: SharpModule | null = null;
48
55
 
@@ -175,7 +182,7 @@ export async function processImage(srcPath: string, cacheImgDir: string, config:
175
182
  const quality = config.quality[format as keyof ImageConfig["quality"]] ?? 80;
176
183
  const task = sharp(srcPath)
177
184
  .resize(width)
178
- .toFormat(format as keyof FormatEnum, { quality })
185
+ .toFormat(format as SharpFormat, { quality })
179
186
  .toFile(absolutePath)
180
187
  .then(() => {});
181
188
 
@@ -340,7 +340,7 @@ async function transformImgNode(
340
340
  imageRefs.set(absoluteSrc, manifest);
341
341
  }
342
342
 
343
- const preferredFormat = config.formats.includes("avif") ? "avif" : config.formats[0];
343
+ const preferredFormat = config.formats.includes("avif") ? "avif" : config.formats[0]!;
344
344
  srcset = buildSrcset(manifest.variants, preferredFormat);
345
345
  }
346
346
  }
@@ -384,8 +384,8 @@ async function transformInnerHtmlImages(
384
384
  const replacements = [];
385
385
 
386
386
  for (const m of html.matchAll(IMG_TAG_RE)) {
387
- const [tag] = m;
388
- const [, attrs] = m;
387
+ const tag = m[0]!;
388
+ const attrs = m[1]!;
389
389
 
390
390
  if (SRCSET_ATTR_RE.test(attrs)) {
391
391
  continue;
@@ -400,7 +400,7 @@ async function transformInnerHtmlImages(
400
400
  }
401
401
 
402
402
  // Attribute values in pre-rendered innerHTML are entity-escaped — decode for processing
403
- const src = srcMatch[1].replaceAll("&amp;", "&");
403
+ const src = srcMatch[1]!.replaceAll("&amp;", "&");
404
404
  const remote = isAllowedRemote(src, config);
405
405
  if (!remote && shouldSkip(src)) {
406
406
  continue;
@@ -425,7 +425,7 @@ async function transformInnerHtmlImages(
425
425
  imageRefs.set(absoluteSrc as string, manifest);
426
426
  }
427
427
 
428
- const preferredFormat = config.formats.includes("avif") ? "avif" : config.formats[0];
428
+ const preferredFormat = config.formats.includes("avif") ? "avif" : config.formats[0]!;
429
429
  srcset = buildSrcset(manifest.variants, preferredFormat);
430
430
  }
431
431
  if (!srcset) {
@@ -169,7 +169,7 @@ async function fileToRoute(relativePath: string, absolutePath: string, registry?
169
169
  // Convert [param] → :param and [...param] → *
170
170
  const urlPattern = urlPath.replaceAll(
171
171
  /\[\.\.\.(\w+)\]|\[(\w+)\]/g,
172
- (match: string, spread: string, named: string) => {
172
+ (_match: string, spread: string, named: string) => {
173
173
  if (spread) {
174
174
  isCatchAll = true;
175
175
  isDynamic = true;
@@ -319,10 +319,9 @@ function resolvePathEntries(
319
319
  // Data file ref: { "$ref": "./data/products.json", param: "id", field: "sku" }
320
320
  if ("$ref" in $paths && $paths.$ref) {
321
321
  const filePath = resolve(projectRoot, $paths.$ref);
322
- /** @type {Record<string, unknown>[]} */
323
- let data;
322
+ let data: unknown;
324
323
  try {
325
- data = JSON.parse(readFileSync(filePath, "utf8"));
324
+ data = JSON.parse(readFileSync(filePath, "utf8")) as unknown;
326
325
  } catch (error) {
327
326
  const err = error as Error;
328
327
  console.warn(`Warning: $paths.$ref could not load "${$paths.$ref}": ${err.message}`);
@@ -334,7 +333,7 @@ function resolvePathEntries(
334
333
  }
335
334
  const param = $paths.param ?? "id";
336
335
  const field = $paths.field ?? "id";
337
- return data.map((item: Record<string, unknown>) => ({
336
+ return (data as Record<string, unknown>[]).map((item: Record<string, unknown>) => ({
338
337
  [param]: item[field] ?? item.id ?? String(item),
339
338
  }));
340
339
  }