zopia 0.5.1 โ†’ 0.5.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.2] - 2026-09-30
11
+
12
+ ### ๐Ÿ› Fixed
13
+ - ๐Ÿ› **`toJSON` property error in generated `index.ts` files (#5).** The
14
+ canonical-JSON helper emitted into schemas using `uniqueItems` (and object
15
+ `const`/`enum` comparisons) guarded against `toJSON` methods with
16
+ `typeof value.toJSON === 'function'`. Because the helper is invoked per
17
+ item with the item's own type, primitive element types (e.g.
18
+ `uniqueItems` on a string array) let TypeScript narrow `value` to `never`,
19
+ and editors reported `TS2339: Property 'toJSON' does not exist on type
20
+ 'never'`. The guard now reads `typeof Object(value).toJSON === 'function'`
21
+ โ€” identical runtime behavior (short-circuited for non-objects,
22
+ `Object(obj) === obj` for objects), but valid TypeScript for every element
23
+ type. Generated code stays plain JavaScript (no type annotations were
24
+ added), and a new suite typechecks generated endpoints end-to-end under
25
+ `strict: true` with `ts.createProgram`, so generated files are now held to
26
+ the same compile-cleanly bar as the IntelliSense declarations.
27
+
10
28
  ## [0.5.1] - 2026-09-30
11
29
 
12
30
  ### ๐Ÿ› Fixed
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![Runtime](https://img.shields.io/badge/Runtime-Bun%201.x-black.svg)](https://bun.sh/)
14
14
  [![Tests](https://img.shields.io/badge/Tests-vitest-10b981.svg)](https://vitest.dev/)
15
15
 
16
- โœ… **Status โ€” Phase 3 complete ยท v0.5.1 released**
16
+ โœ… **Status โ€” Phase 3 complete ยท v0.5.2 released**
17
17
 
18
18
  </div>
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zopia",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Type-safe OpenAPI, JSON Schema, and Zod conversion toolkit.",
5
5
  "keywords": [
6
6
  "api-docs",
@@ -310,7 +310,7 @@ function jsonSchemaToZodInternal(input: JsonSchema | string, options: JsonSchema
310
310
  const normalized = JSON.parse(literal);
311
311
  return JSON.stringify(normalized, (_key, item) => item && typeof item === 'object' && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) : item);
312
312
  };
313
- const generatedCanonicalJson = `(value) => { if (value && typeof value === 'object' && typeof value.toJSON === 'function') return undefined; try { return JSON.stringify(value, (_key, item) => { if (item === null || typeof item === 'string' || typeof item === 'boolean') return item; if (typeof item === 'number') { if (!Number.isFinite(item)) throw new Error('non-JSON number'); return item; } if (!item || typeof item !== 'object' || Object.getOwnPropertySymbols(item).length || (!Array.isArray(item) && ![Object.prototype, null].includes(Object.getPrototypeOf(item)))) throw new Error('non-JSON value'); const children = Array.isArray(item) ? item : Object.values(item); if (children.some((child) => child && typeof child === 'object' && typeof child.toJSON === 'function')) throw new Error('non-JSON toJSON'); if (Array.isArray(item)) { if (Object.keys(item).length !== item.length || !item.every((_child, index) => Object.prototype.hasOwnProperty.call(item, index))) throw new Error('non-JSON array'); return item; } return Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)); }); } catch { return undefined; } }`;
313
+ const generatedCanonicalJson = `(value) => { if (value && typeof value === 'object' && typeof Object(value).toJSON === 'function') return undefined; try { return JSON.stringify(value, (_key, item) => { if (item === null || typeof item === 'string' || typeof item === 'boolean') return item; if (typeof item === 'number') { if (!Number.isFinite(item)) throw new Error('non-JSON number'); return item; } if (!item || typeof item !== 'object' || Object.getOwnPropertySymbols(item).length || (!Array.isArray(item) && ![Object.prototype, null].includes(Object.getPrototypeOf(item)))) throw new Error('non-JSON value'); const children = Array.isArray(item) ? item : Object.values(item); if (children.some((child) => child && typeof child === 'object' && typeof child.toJSON === 'function')) throw new Error('non-JSON toJSON'); if (Array.isArray(item)) { if (Object.keys(item).length !== item.length || !item.every((_child, index) => Object.prototype.hasOwnProperty.call(item, index))) throw new Error('non-JSON array'); return item; } return Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)); }); } catch { return undefined; } }`;
314
314
  let source: JsonSchema;
315
315
  try {
316
316
  if (typeof input !== 'string') source = input;
@@ -16,7 +16,7 @@ export const ZOPIA_MANIFEST_SCHEMA = 'zopia:manifest@1' as const;
16
16
  export const ZOPIA_MANIFEST_FILE = '.zopia-manifest.json' as const;
17
17
 
18
18
  /** Package version recorded by the current manifest writer. */
19
- export const ZOPIA_VERSION = '0.5.1' as const;
19
+ export const ZOPIA_VERSION = '0.5.2' as const;
20
20
 
21
21
  /** Supported source dialect labels stored in a manifest. */
22
22
  export type ZopiaManifestSourceKind = 'swagger-2.0' | 'openapi-3.0' | 'openapi-3.1';