@caretcms/zod 0.1.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/README.md +34 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @caretcms/zod
|
|
2
|
+
|
|
3
|
+
Derive [CaretCMS](https://github.com/web-stacked/caretcms) Studio schemas from the Zod schemas you already have.
|
|
4
|
+
|
|
5
|
+
CaretCMS's Studio consumes plain JSON Schema, and `@caretcms/core` stays deliberately Zod-agnostic (zero runtime deps). If you already describe a content collection with Zod — e.g. in Astro's `content.config.ts` — this helper turns that one source into the JSON-Schema map you hand to `caret({ schemas })`, so field definitions, labels, and widget hints live in a single place.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @caretcms/zod zod
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// astro.config.mjs
|
|
13
|
+
import caret from '@caretcms/core';
|
|
14
|
+
import { schemasFromZod } from '@caretcms/zod';
|
|
15
|
+
import { blogSchema, pageSchema } from './src/schemas.mjs';
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
integrations: [
|
|
19
|
+
caret({ schemas: schemasFromZod({ blog: blogSchema, page: pageSchema }) }),
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
What the conversion does beyond `z.toJSONSchema`:
|
|
25
|
+
|
|
26
|
+
- **Dates** (`z.date()` / `z.coerce.date()`) become `{ type: "string", format: "date" }` so the Studio renders a date widget.
|
|
27
|
+
- **`description` → `title`**: Zod `.describe("Headline")` becomes the field's Studio label (an explicit `title` wins; `description` is kept).
|
|
28
|
+
- **`override` hook** for per-node tweaks: `schemaFromZod(schema, { override: ({ jsonSchema }) => { … } })`.
|
|
29
|
+
|
|
30
|
+
API: `schemaFromZod(zodObjectSchema, options?)` for one collection, `schemasFromZod({ name: schema, … }, options?)` for the whole map.
|
|
31
|
+
|
|
32
|
+
Requires Zod v4 (peer dependency, uses `z.toJSONSchema`). Nothing here is imported by `@caretcms/core` — its Zod-agnostic boundary stays intact.
|
|
33
|
+
|
|
34
|
+
MIT © CaretCMS contributors
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @caretcms/zod — derive CaretCMS Studio schemas from your Zod content-collection schemas.
|
|
3
|
+
*
|
|
4
|
+
* CaretCMS's Studio consumes plain JSON Schema, and `@caretcms/core` stays
|
|
5
|
+
* deliberately Zod-agnostic (zero runtime deps). If you already describe a
|
|
6
|
+
* content collection with Zod (Astro's `content.config.ts`), this helper turns
|
|
7
|
+
* that ONE source into the JSON-Schema map you hand to `caret({ schemas })` — so
|
|
8
|
+
* field definitions, labels, and widget hints live in a single place instead of
|
|
9
|
+
* being duplicated in a hand-written `caret.schemas` file.
|
|
10
|
+
*
|
|
11
|
+
* import { schemasFromZod } from "@caretcms/zod";
|
|
12
|
+
* caret({ schemas: schemasFromZod({ blog: blogSchema, page: pageSchema }) });
|
|
13
|
+
*
|
|
14
|
+
* Requires Zod v4 (uses `z.toJSONSchema`). Zod is a PEER dependency; nothing here
|
|
15
|
+
* is imported by `@caretcms/core`, preserving its Zod-agnostic boundary.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
/** A JSON Schema object node. Structurally what core's `CollectionSchema`
|
|
19
|
+
* expects: `{ type: "object", properties: { ... } }`. */
|
|
20
|
+
export type JsonSchema = Record<string, unknown>;
|
|
21
|
+
export interface DeriveOptions {
|
|
22
|
+
/** Extra per-node tweaks, applied AFTER the built-in date handling. Use this to
|
|
23
|
+
* map project-specific Zod shapes to CaretCMS widget `format` hints. */
|
|
24
|
+
override?: (ctx: {
|
|
25
|
+
zodSchema: unknown;
|
|
26
|
+
jsonSchema: JsonSchema;
|
|
27
|
+
}) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Convert ONE Zod object schema into a CaretCMS-ready JSON Schema.
|
|
31
|
+
*
|
|
32
|
+
* Bridges the gaps a raw `z.toJSONSchema` leaves for the Studio:
|
|
33
|
+
* - `z.date()` / `z.coerce.date()` → `{ type: "string", format: "date" }`.
|
|
34
|
+
* (Raw conversion throws "Date cannot be represented in JSON Schema" — the
|
|
35
|
+
* stored value is an ISO string, so a string with a date widget is correct.)
|
|
36
|
+
* - `description` → `title`, so each field gets a friendly label.
|
|
37
|
+
* - Custom widget hints (`image`, `html`, …) travel via `.meta({ format: "…" })`.
|
|
38
|
+
*
|
|
39
|
+
* Throws if the schema is not a Zod object — a collection schema must be an object.
|
|
40
|
+
*/
|
|
41
|
+
export declare function schemaFromZod(schema: z.ZodType, options?: DeriveOptions): JsonSchema;
|
|
42
|
+
/**
|
|
43
|
+
* Convert a map of `collectionName -> Zod object schema` into the
|
|
44
|
+
* `Record<string, JsonSchema>` you pass to `caret({ schemas })`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function schemasFromZod(schemas: Record<string, z.ZodType>, options?: DeriveOptions): Record<string, JsonSchema>;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;0DAC0D;AAC1D,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B;6EACyE;IACzE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;CAC1E;AAwBD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,GAAE,aAAkB,GAAG,UAAU,CAuBxF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAClC,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAM5B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @caretcms/zod — derive CaretCMS Studio schemas from your Zod content-collection schemas.
|
|
3
|
+
*
|
|
4
|
+
* CaretCMS's Studio consumes plain JSON Schema, and `@caretcms/core` stays
|
|
5
|
+
* deliberately Zod-agnostic (zero runtime deps). If you already describe a
|
|
6
|
+
* content collection with Zod (Astro's `content.config.ts`), this helper turns
|
|
7
|
+
* that ONE source into the JSON-Schema map you hand to `caret({ schemas })` — so
|
|
8
|
+
* field definitions, labels, and widget hints live in a single place instead of
|
|
9
|
+
* being duplicated in a hand-written `caret.schemas` file.
|
|
10
|
+
*
|
|
11
|
+
* import { schemasFromZod } from "@caretcms/zod";
|
|
12
|
+
* caret({ schemas: schemasFromZod({ blog: blogSchema, page: pageSchema }) });
|
|
13
|
+
*
|
|
14
|
+
* Requires Zod v4 (uses `z.toJSONSchema`). Zod is a PEER dependency; nothing here
|
|
15
|
+
* is imported by `@caretcms/core`, preserving its Zod-agnostic boundary.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
function isObjectSchema(node) {
|
|
19
|
+
return node.type === "object" && typeof node.properties === "object" && node.properties !== null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Promote Zod `description` → JSON Schema `title` (the Studio renders `title` as
|
|
23
|
+
* the field label). Recurses into nested object properties and array items.
|
|
24
|
+
* Non-destructive: an explicit `title` wins, and `description` is left intact.
|
|
25
|
+
*/
|
|
26
|
+
function promoteTitles(node) {
|
|
27
|
+
if (!node || typeof node !== "object")
|
|
28
|
+
return;
|
|
29
|
+
if (typeof node.description === "string" && typeof node.title !== "string") {
|
|
30
|
+
node.title = node.description;
|
|
31
|
+
}
|
|
32
|
+
const props = node.properties;
|
|
33
|
+
if (props && typeof props === "object") {
|
|
34
|
+
for (const child of Object.values(props))
|
|
35
|
+
promoteTitles(child);
|
|
36
|
+
}
|
|
37
|
+
const items = node.items;
|
|
38
|
+
if (items && typeof items === "object")
|
|
39
|
+
promoteTitles(items);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Convert ONE Zod object schema into a CaretCMS-ready JSON Schema.
|
|
43
|
+
*
|
|
44
|
+
* Bridges the gaps a raw `z.toJSONSchema` leaves for the Studio:
|
|
45
|
+
* - `z.date()` / `z.coerce.date()` → `{ type: "string", format: "date" }`.
|
|
46
|
+
* (Raw conversion throws "Date cannot be represented in JSON Schema" — the
|
|
47
|
+
* stored value is an ISO string, so a string with a date widget is correct.)
|
|
48
|
+
* - `description` → `title`, so each field gets a friendly label.
|
|
49
|
+
* - Custom widget hints (`image`, `html`, …) travel via `.meta({ format: "…" })`.
|
|
50
|
+
*
|
|
51
|
+
* Throws if the schema is not a Zod object — a collection schema must be an object.
|
|
52
|
+
*/
|
|
53
|
+
export function schemaFromZod(schema, options = {}) {
|
|
54
|
+
const json = z.toJSONSchema(schema, {
|
|
55
|
+
unrepresentable: "any",
|
|
56
|
+
io: "input",
|
|
57
|
+
override: (ctx) => {
|
|
58
|
+
const def = ctx.zodSchema?._zod?.def;
|
|
59
|
+
if (def?.type === "date") {
|
|
60
|
+
ctx.jsonSchema.type = "string";
|
|
61
|
+
ctx.jsonSchema.format = "date";
|
|
62
|
+
}
|
|
63
|
+
options.override?.(ctx);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
delete json.$schema;
|
|
67
|
+
promoteTitles(json);
|
|
68
|
+
if (!isObjectSchema(json)) {
|
|
69
|
+
throw new Error("@caretcms/zod: each collection schema must be a Zod object (z.object({ ... })).");
|
|
70
|
+
}
|
|
71
|
+
return json;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert a map of `collectionName -> Zod object schema` into the
|
|
75
|
+
* `Record<string, JsonSchema>` you pass to `caret({ schemas })`.
|
|
76
|
+
*/
|
|
77
|
+
export function schemasFromZod(schemas, options = {}) {
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [collection, schema] of Object.entries(schemas)) {
|
|
80
|
+
out[collection] = schemaFromZod(schema, options);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,SAAS,cAAc,CAAC,IAAgB;IACtC,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC;AACnG,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAgB;IACrC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO;IAC9C,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3E,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC;IAChC,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAoD,CAAC;IACxE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAA+B,CAAC;IACnD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,aAAa,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,UAAyB,EAAE;IAC1E,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;QAClC,eAAe,EAAE,KAAK;QACtB,EAAE,EAAE,OAAO;QACX,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE;YAChB,MAAM,GAAG,GAAI,GAAG,CAAC,SAAoD,EAAE,IAAI,EAAE,GAAG,CAAC;YACjF,IAAI,GAAG,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzB,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,QAAQ,CAAC;gBAC/B,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;YACjC,CAAC;YACD,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAqD,CAAC,CAAC;QAC5E,CAAC;KACF,CAAe,CAAC;IAEjB,OAAO,IAAI,CAAC,OAAO,CAAC;IACpB,aAAa,CAAC,IAAI,CAAC,CAAC;IAEpB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAkC,EAClC,UAAyB,EAAE;IAE3B,MAAM,GAAG,GAA+B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caretcms/zod",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Derive CaretCMS Studio schemas from your Zod content-collection schemas — single source of truth.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"astro",
|
|
9
|
+
"cms",
|
|
10
|
+
"caret",
|
|
11
|
+
"zod",
|
|
12
|
+
"json-schema",
|
|
13
|
+
"content-collections"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/web-stacked/caretcms/tree/main/packages/zod#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/web-stacked/caretcms.git",
|
|
19
|
+
"directory": "packages/zod"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/web-stacked/caretcms/issues"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
38
|
+
"clean": "rm -rf dist",
|
|
39
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"zod": "^4.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "25.6.0",
|
|
49
|
+
"typescript": "6.0.3",
|
|
50
|
+
"zod": "4.4.3"
|
|
51
|
+
}
|
|
52
|
+
}
|