@schmock/openapi 1.1.1 → 1.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa/C;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,SAAS,GAAG,UAAU,GAChC,WAAW,CAEb"}
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,SAAS,GAAG,UAAU,GAChC,WAAW,CAEb"}
@@ -1,13 +1,5 @@
1
1
  /// <reference path="../../../types/schmock.d.ts" />
2
- function isRecord(value) {
3
- return typeof value === "object" && value !== null && !Array.isArray(value);
4
- }
5
- function toJsonSchema(node) {
6
- // Object.assign merges unknown-keyed properties into JSONSchema7.
7
- // This is safe because the normalizer has already ensured the shape
8
- // is valid JSON Schema 7 before calling this function.
9
- return Object.assign({}, node);
10
- }
2
+ import { isRecord, toJsonSchema } from "./utils.js";
11
3
  /**
12
4
  * Normalize an OpenAPI schema to pure JSON Schema 7 that json-schema-faker understands.
13
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAkDD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAuH5E"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AA8CD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAuH5E"}
package/dist/parser.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import SwaggerParser from "@apidevtools/swagger-parser";
3
3
  import { toHttpMethod } from "@schmock/core";
4
4
  import { normalizeSchema } from "./normalizer.js";
5
+ import { isRecord } from "./utils.js";
5
6
  const HTTP_METHOD_KEYS = new Set([
6
7
  "get",
7
8
  "post",
@@ -11,9 +12,6 @@ const HTTP_METHOD_KEYS = new Set([
11
12
  "head",
12
13
  "options",
13
14
  ]);
14
- function isRecord(value) {
15
- return typeof value === "object" && value !== null && !Array.isArray(value);
16
- }
17
15
  function isOpenApiDocument(value) {
18
16
  return isRecord(value) && ("swagger" in value || "openapi" in value);
19
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGxD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAMvC,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,sCAAsC;IACtC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wCAAwC;IACxC,aAAa,CAAC,EAAE;QACd,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAwCzB"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAIxD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,sCAAsC;IACtC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wCAAwC;IACxC,aAAa,CAAC,EAAE;QACd,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAwCzB"}
package/dist/plugin.js CHANGED
@@ -3,9 +3,7 @@ import { detectCrudResources } from "./crud-detector.js";
3
3
  import { createCreateGenerator, createDeleteGenerator, createListGenerator, createReadGenerator, createStaticGenerator, createUpdateGenerator, } from "./generators.js";
4
4
  import { parseSpec } from "./parser.js";
5
5
  import { loadSeed } from "./seed.js";
6
- function isRecord(value) {
7
- return typeof value === "object" && value !== null && !Array.isArray(value);
8
- }
6
+ import { isRecord } from "./utils.js";
9
7
  /**
10
8
  * Create an OpenAPI plugin that auto-registers CRUD routes from a spec.
11
9
  *
@@ -26,7 +24,7 @@ export async function openapi(options) {
26
24
  : new Map();
27
25
  return {
28
26
  name: "@schmock/openapi",
29
- version: "1.0.0",
27
+ version: "1.1.1",
30
28
  install(instance) {
31
29
  // Seed initial state — we need state to be initialized before registering routes
32
30
  // The state is populated via generator context.state on first request,
@@ -1 +1 @@
1
- {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,MAAM,UAAU,GAAG,OAAO,EAAE,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,YAAY,EAAE,GACxB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAwCxB"}
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,MAAM,UAAU,GAAG,OAAO,EAAE,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,YAAY,EAAE,GACxB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAyDxB"}
package/dist/seed.js CHANGED
@@ -20,7 +20,13 @@ export function loadSeed(config, resources) {
20
20
  else if (typeof source === "string") {
21
21
  // File path
22
22
  const content = readFileSync(source, "utf-8");
23
- const parsed = JSON.parse(content);
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(content);
26
+ }
27
+ catch {
28
+ throw new Error(`Seed file "${source}" for resource "${resourceName}" contains invalid JSON`);
29
+ }
24
30
  if (!Array.isArray(parsed)) {
25
31
  throw new Error(`Seed file "${source}" for resource "${resourceName}" must contain a JSON array`);
26
32
  }
@@ -30,10 +36,16 @@ export function loadSeed(config, resources) {
30
36
  source !== null &&
31
37
  "count" in source) {
32
38
  // Auto-generate from schema
39
+ const rawCount = source.count;
40
+ if (typeof rawCount !== "number" ||
41
+ !Number.isInteger(rawCount) ||
42
+ rawCount < 0) {
43
+ throw new Error(`Seed count for "${resourceName}" must be a non-negative integer, got: ${String(rawCount)}`);
44
+ }
33
45
  if (!resource?.schema) {
34
46
  throw new Error(`Cannot auto-generate seed for "${resourceName}": no schema found in spec`);
35
47
  }
36
- const items = generateSeedItems(resource.schema, source.count, resource.idParam);
48
+ const items = generateSeedItems(resource.schema, rawCount, resource.idParam);
37
49
  result.set(resourceName, items);
38
50
  }
39
51
  }
@@ -0,0 +1,4 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ export declare function toJsonSchema(node: Record<string, unknown>): JSONSchema7;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAEvE"}
package/dist/utils.js ADDED
@@ -0,0 +1,6 @@
1
+ export function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ export function toJsonSchema(node) {
5
+ return Object.assign({}, node);
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schmock/openapi",
3
3
  "description": "OpenAPI/Swagger spec-driven auto-registration plugin for Schmock",
4
- "version": "1.1.1",
4
+ "version": "1.1.2",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -38,8 +38,8 @@
38
38
  "devDependencies": {
39
39
  "@amiceli/vitest-cucumber": "^6.2.0",
40
40
  "@types/json-schema": "^7.0.15",
41
- "@types/node": "^25.1.0",
41
+ "@types/node": "^25.2.1",
42
42
  "openapi-types": "^12.1.3",
43
- "vitest": "^4.0.15"
43
+ "vitest": "^4.0.18"
44
44
  }
45
45
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import type { JSONSchema7 } from "json-schema";
4
4
  import type { ParsedPath } from "./parser.js";
5
+ import { isRecord, toJsonSchema } from "./utils.js";
5
6
 
6
7
  export type CrudOperation = "list" | "create" | "read" | "update" | "delete";
7
8
 
@@ -110,8 +111,8 @@ function buildResource(
110
111
  const items = Array.isArray(listSchema.items)
111
112
  ? listSchema.items[0]
112
113
  : listSchema.items;
113
- if (typeof items === "object" && items !== null) {
114
- schema = schema ?? (items as JSONSchema7);
114
+ if (isRecord(items)) {
115
+ schema = schema ?? toJsonSchema(items);
115
116
  }
116
117
  }
117
118
  } else if (p.method === "POST") {
package/src/generators.ts CHANGED
@@ -4,13 +4,10 @@ import { generateFromSchema } from "@schmock/schema";
4
4
  import type { JSONSchema7 } from "json-schema";
5
5
  import type { CrudResource } from "./crud-detector.js";
6
6
  import type { ParsedPath } from "./parser.js";
7
+ import { isRecord } from "./utils.js";
7
8
 
8
9
  const COLLECTION_STATE_PREFIX = "openapi:collections:";
9
10
 
10
- function isRecord(value: unknown): value is Record<string, unknown> {
11
- return typeof value === "object" && value !== null && !Array.isArray(value);
12
- }
13
-
14
11
  function toTuple(status: number, body: unknown): [number, unknown] {
15
12
  return [status, body];
16
13
  }
@@ -159,9 +156,11 @@ export function createStaticGenerator(
159
156
  if (responseSchema) {
160
157
  try {
161
158
  return generateFromSchema({ schema: responseSchema });
162
- } catch {
163
- // Complex schemas (deep anyOf, circular refs) may fail generation.
164
- // Fall back to an empty object rather than crashing.
159
+ } catch (error) {
160
+ console.warn(
161
+ `[@schmock/openapi] Schema generation failed for ${parsedPath.method} ${parsedPath.path}:`,
162
+ error instanceof Error ? error.message : error,
163
+ );
165
164
  return {};
166
165
  }
167
166
  }
package/src/normalizer.ts CHANGED
@@ -1,17 +1,7 @@
1
1
  /// <reference path="../../../types/schmock.d.ts" />
2
2
 
3
3
  import type { JSONSchema7 } from "json-schema";
4
-
5
- function isRecord(value: unknown): value is Record<string, unknown> {
6
- return typeof value === "object" && value !== null && !Array.isArray(value);
7
- }
8
-
9
- function toJsonSchema(node: Record<string, unknown>): JSONSchema7 {
10
- // Object.assign merges unknown-keyed properties into JSONSchema7.
11
- // This is safe because the normalizer has already ensured the shape
12
- // is valid JSON Schema 7 before calling this function.
13
- return Object.assign<JSONSchema7, Record<string, unknown>>({}, node);
14
- }
4
+ import { isRecord, toJsonSchema } from "./utils.js";
15
5
 
16
6
  /**
17
7
  * Normalize an OpenAPI schema to pure JSON Schema 7 that json-schema-faker understands.
package/src/parser.ts CHANGED
@@ -5,6 +5,7 @@ import { toHttpMethod } from "@schmock/core";
5
5
  import type { JSONSchema7 } from "json-schema";
6
6
  import type { OpenAPI } from "openapi-types";
7
7
  import { normalizeSchema } from "./normalizer.js";
8
+ import { isRecord } from "./utils.js";
8
9
 
9
10
  export interface ParsedSpec {
10
11
  title: string;
@@ -41,10 +42,6 @@ const HTTP_METHOD_KEYS = new Set([
41
42
  "options",
42
43
  ]);
43
44
 
44
- function isRecord(value: unknown): value is Record<string, unknown> {
45
- return typeof value === "object" && value !== null && !Array.isArray(value);
46
- }
47
-
48
45
  function isOpenApiDocument(value: unknown): value is OpenAPI.Document {
49
46
  return isRecord(value) && ("swagger" in value || "openapi" in value);
50
47
  }
package/src/plugin.ts CHANGED
@@ -13,13 +13,10 @@ import {
13
13
  import { parseSpec } from "./parser.js";
14
14
  import type { SeedConfig, SeedSource } from "./seed.js";
15
15
  import { loadSeed } from "./seed.js";
16
+ import { isRecord } from "./utils.js";
16
17
 
17
18
  export type { SeedConfig, SeedSource };
18
19
 
19
- function isRecord(value: unknown): value is Record<string, unknown> {
20
- return typeof value === "object" && value !== null && !Array.isArray(value);
21
- }
22
-
23
20
  export interface OpenApiOptions {
24
21
  /** File path or inline spec object */
25
22
  spec: string | object;
@@ -60,7 +57,7 @@ export async function openapi(
60
57
 
61
58
  return {
62
59
  name: "@schmock/openapi",
63
- version: "1.0.0",
60
+ version: "1.1.1",
64
61
 
65
62
  install(instance: Schmock.CallableMockInstance) {
66
63
  // Seed initial state — we need state to be initialized before registering routes
package/src/seed.ts CHANGED
@@ -31,7 +31,14 @@ export function loadSeed(
31
31
  } else if (typeof source === "string") {
32
32
  // File path
33
33
  const content = readFileSync(source, "utf-8");
34
- const parsed: unknown = JSON.parse(content);
34
+ let parsed: unknown;
35
+ try {
36
+ parsed = JSON.parse(content);
37
+ } catch {
38
+ throw new Error(
39
+ `Seed file "${source}" for resource "${resourceName}" contains invalid JSON`,
40
+ );
41
+ }
35
42
  if (!Array.isArray(parsed)) {
36
43
  throw new Error(
37
44
  `Seed file "${source}" for resource "${resourceName}" must contain a JSON array`,
@@ -44,6 +51,16 @@ export function loadSeed(
44
51
  "count" in source
45
52
  ) {
46
53
  // Auto-generate from schema
54
+ const rawCount = source.count;
55
+ if (
56
+ typeof rawCount !== "number" ||
57
+ !Number.isInteger(rawCount) ||
58
+ rawCount < 0
59
+ ) {
60
+ throw new Error(
61
+ `Seed count for "${resourceName}" must be a non-negative integer, got: ${String(rawCount)}`,
62
+ );
63
+ }
47
64
  if (!resource?.schema) {
48
65
  throw new Error(
49
66
  `Cannot auto-generate seed for "${resourceName}": no schema found in spec`,
@@ -51,7 +68,7 @@ export function loadSeed(
51
68
  }
52
69
  const items = generateSeedItems(
53
70
  resource.schema,
54
- source.count,
71
+ rawCount,
55
72
  resource.idParam,
56
73
  );
57
74
  result.set(resourceName, items);
@@ -1,4 +1,5 @@
1
1
  import { resolve } from "node:path";
2
+ import { writeFileSync } from "node:fs";
2
3
  import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
3
4
  import { schmock } from "@schmock/core";
4
5
  import { expect } from "vitest";
@@ -7,9 +8,12 @@ import { openapi } from "../plugin";
7
8
  const feature = await loadFeature("../../features/openapi-seed.feature");
8
9
  const fixturesDir = resolve(import.meta.dirname, "../__fixtures__");
9
10
 
11
+ const scratchDir = resolve(import.meta.dirname, "../__fixtures__");
12
+
10
13
  describeFeature(feature, ({ Scenario }) => {
11
14
  let mock: Schmock.CallableMockInstance;
12
15
  let response: Schmock.Response;
16
+ let specPath: string;
13
17
 
14
18
  Scenario("Seed with inline objects", ({ Given, When, Then }) => {
15
19
  Given("a mock with Petstore spec and inline seed data", async () => {
@@ -91,4 +95,53 @@ describeFeature(feature, ({ Scenario }) => {
91
95
  });
92
96
  },
93
97
  );
98
+
99
+ Scenario(
100
+ "Invalid seed count produces descriptive error",
101
+ ({ Given, Then }) => {
102
+ Given("a Petstore spec path", () => {
103
+ specPath = `${fixturesDir}/petstore-swagger2.json`;
104
+ });
105
+
106
+ Then(
107
+ 'creating a mock with seed count "abc" should throw about non-negative integer',
108
+ async () => {
109
+ await expect(
110
+ openapi({
111
+ spec: specPath,
112
+ seed: { pets: { count: "abc" as any } },
113
+ }),
114
+ ).rejects.toThrow("non-negative integer");
115
+ },
116
+ );
117
+ },
118
+ );
119
+
120
+ Scenario(
121
+ "Malformed seed file produces descriptive error",
122
+ ({ Given, Then }) => {
123
+ Given("a Petstore spec path", () => {
124
+ specPath = `${fixturesDir}/petstore-swagger2.json`;
125
+ });
126
+
127
+ Then(
128
+ "creating a mock with malformed seed file should throw about invalid JSON",
129
+ async () => {
130
+ const badFile = resolve(scratchDir, "__bad-seed-temp.json");
131
+ writeFileSync(badFile, "NOT VALID JSON{{{");
132
+ try {
133
+ await expect(
134
+ openapi({
135
+ spec: specPath,
136
+ seed: { pets: badFile },
137
+ }),
138
+ ).rejects.toThrow("invalid JSON");
139
+ } finally {
140
+ const { unlinkSync } = await import("node:fs");
141
+ unlinkSync(badFile);
142
+ }
143
+ },
144
+ );
145
+ },
146
+ );
94
147
  });
package/src/utils.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+
3
+ export function isRecord(value: unknown): value is Record<string, unknown> {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+
7
+ export function toJsonSchema(node: Record<string, unknown>): JSONSchema7 {
8
+ return Object.assign<JSONSchema7, Record<string, unknown>>({}, node);
9
+ }