@contentrail/core 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +164 -2
- package/dist/bounds.d.ts +36 -0
- package/dist/bounds.d.ts.map +1 -0
- package/dist/bounds.js +83 -0
- package/dist/bounds.js.map +1 -0
- package/dist/collection.d.ts +7 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +45 -0
- package/dist/collection.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +28 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +170 -0
- package/dist/validate.js.map +1 -0
- package/package.json +49 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContentRail contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,165 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@contentrail/core`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-independent ContentRail schemas, validation, and inferred content types.
|
|
4
|
+
|
|
5
|
+
This package defines **collections and validation only**. It does not read files, talk to networks, store content, authenticate users, or publish sites.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js **≥ 22** (`engines.node`)
|
|
10
|
+
- TypeScript 5.9+ recommended for consumers
|
|
11
|
+
- [Zod](https://zod.dev) `^4.6.7` (dependency + peer; native `zod` entry — not `zod/v3`)
|
|
12
|
+
|
|
13
|
+
Prefer `validateEntry` / `validateCollection` / `assertValidEntry` for untrusted
|
|
14
|
+
input. Calling `schema.safeParse()` directly bypasses ContentRail JSON bounds.
|
|
15
|
+
|
|
16
|
+
Upgrading from ContentRail `0.1.0` (Zod 3): see `docs/MIGRATION_ZOD4.md`.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
### Monorepo (current)
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm install
|
|
24
|
+
pnpm --filter @contentrail/core build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### From npm (upcoming `0.2.0` after authorized publish)
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pnpm add @contentrail/core@0.2.0 zod@^4.6.7
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Define a collection
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { defineCollection } from "@contentrail/core";
|
|
37
|
+
import { z } from "zod";
|
|
38
|
+
|
|
39
|
+
const articles = defineCollection({
|
|
40
|
+
name: "articles",
|
|
41
|
+
schema: z.object({
|
|
42
|
+
title: z.string().min(1),
|
|
43
|
+
slug: z.string().min(1),
|
|
44
|
+
published: z.boolean(),
|
|
45
|
+
publishedAt: z.string().optional(),
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Collection names must match `^[a-z][a-z0-9_-]*$`.
|
|
51
|
+
|
|
52
|
+
### Unexpected fields
|
|
53
|
+
|
|
54
|
+
| `unknownKeys` | Behaviour |
|
|
55
|
+
| ------------------- | ----------------------------------------- |
|
|
56
|
+
| `"strip"` (default) | Unknown root-object keys are removed. |
|
|
57
|
+
| `"strict"` | Unknown root-object keys fail validation. |
|
|
58
|
+
|
|
59
|
+
The policy is applied when the schema is a Zod object. Non-object schemas are left unchanged.
|
|
60
|
+
|
|
61
|
+
## Validate an entry
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { validateEntry } from "@contentrail/core";
|
|
65
|
+
|
|
66
|
+
const result = validateEntry(articles, {
|
|
67
|
+
title: "Welcome",
|
|
68
|
+
slug: "welcome",
|
|
69
|
+
published: true,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
console.error(result.error.issues);
|
|
74
|
+
} else {
|
|
75
|
+
console.log(result.data.title);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`validateEntry` never throws for invalid content. Use `assertValidEntry` when you prefer exceptions.
|
|
80
|
+
|
|
81
|
+
## Validate a collection (array of entries)
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { validateCollection } from "@contentrail/core";
|
|
85
|
+
|
|
86
|
+
const result = validateCollection(articles, [
|
|
87
|
+
{ title: "Welcome", slug: "welcome", published: true },
|
|
88
|
+
{ title: "Next", slug: "next", published: false },
|
|
89
|
+
]);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Failing issues are prefixed with the entry index, for example `1.title`.
|
|
93
|
+
|
|
94
|
+
## Type inference
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import type { InferEntry } from "@contentrail/core";
|
|
98
|
+
|
|
99
|
+
type Article = InferEntry<typeof articles>;
|
|
100
|
+
// { title: string; slug: string; published: boolean; publishedAt?: string }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## `ContentSchema`
|
|
104
|
+
|
|
105
|
+
`ContentSchema` is the public Zod 4 schema constraint used by
|
|
106
|
+
`defineCollection` / `ContentCollection` generics. It is an alias of
|
|
107
|
+
`z.ZodType` from the native `zod` entry:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { defineCollection, type ContentSchema } from "@contentrail/core";
|
|
111
|
+
import { z } from "zod";
|
|
112
|
+
|
|
113
|
+
const schema: ContentSchema = z.object({
|
|
114
|
+
title: z.string().min(1),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const pages = defineCollection({
|
|
118
|
+
name: "pages",
|
|
119
|
+
schema,
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Prefer defining schemas with `z` and letting `defineCollection` infer types;
|
|
124
|
+
use `ContentSchema` when you need an explicit annotation.
|
|
125
|
+
|
|
126
|
+
## Errors
|
|
127
|
+
|
|
128
|
+
| Error | When |
|
|
129
|
+
| ------------------------ | ----------------------------------------------------------- |
|
|
130
|
+
| `ContentDefinitionError` | Invalid `defineCollection` input (thrown). |
|
|
131
|
+
| `ContentValidationError` | Invalid content (`validate*` result or `assertValidEntry`). |
|
|
132
|
+
|
|
133
|
+
`ContentValidationError` includes:
|
|
134
|
+
|
|
135
|
+
- `collection` — collection name
|
|
136
|
+
- `issues` — `{ path, code, message, expected?, received? }[]`
|
|
137
|
+
- `toJSON()` — stable serializable shape
|
|
138
|
+
|
|
139
|
+
## Public exports
|
|
140
|
+
|
|
141
|
+
From `@contentrail/core`:
|
|
142
|
+
|
|
143
|
+
- `defineCollection`
|
|
144
|
+
- `validateEntry`
|
|
145
|
+
- `validateCollection`
|
|
146
|
+
- `assertValidEntry`
|
|
147
|
+
- `ContentDefinitionError`
|
|
148
|
+
- `ContentValidationError`
|
|
149
|
+
- Types: `ContentSchema`, `ContentCollection`, `DefineCollectionInput`, `InferEntry`, `UnknownKeysPolicy`, `ValidationResult`, `ValidationSuccess`, `ValidationFailure`, `ValidationIssue`
|
|
150
|
+
|
|
151
|
+
There is a single browser-safe export map entry (`.`). No server-only subpath exists in this package.
|
|
152
|
+
|
|
153
|
+
## Compatibility
|
|
154
|
+
|
|
155
|
+
- Runtime: any modern ESM environment (browsers, Node, Workers) that can run Zod
|
|
156
|
+
- Module format: ESM only
|
|
157
|
+
- Not coupled to Astro, Vite, Cloudflare, or React
|
|
158
|
+
|
|
159
|
+
## Current limitations
|
|
160
|
+
|
|
161
|
+
- No content I/O, Git, R2, or publishing APIs
|
|
162
|
+
- No editor schema introspection helpers beyond Zod itself
|
|
163
|
+
- No JSON Schema export
|
|
164
|
+
- Requires Zod **4.6.7+**; Zod 3 schemas are not supported
|
|
165
|
+
- See root README / `docs/PUBLISHING.md` for `0.2.0` publication status
|
package/dist/bounds.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural limits applied **before** Zod validation.
|
|
3
|
+
*
|
|
4
|
+
* ContentRail's public schema API targets Zod **4.6.7+** (native `zod` entry),
|
|
5
|
+
* which remediates GHSA-mrhx-c7xc-m3w5. These bounds remain as defense-in-depth
|
|
6
|
+
* at trust boundaries and provide stable ContentRail error codes (`too_big`,
|
|
7
|
+
* `too_deep`, `cyclic`) independent of Zod issue shaping.
|
|
8
|
+
*
|
|
9
|
+
* Limitations (explicit): these checks bound nesting, array length, and object
|
|
10
|
+
* key count. They do **not** cap string byte length, total serialized size, or
|
|
11
|
+
* CPU spent validating values that are within the structural limits.
|
|
12
|
+
*/
|
|
13
|
+
export interface JsonBoundLimits {
|
|
14
|
+
/** Maximum length of any array in the value tree. Default: 10_000. */
|
|
15
|
+
readonly maxArrayLength?: number;
|
|
16
|
+
/** Maximum number of own keys on any object. Default: 10_000. */
|
|
17
|
+
readonly maxObjectKeys?: number;
|
|
18
|
+
/** Maximum nesting depth (root = 0). Default: 32. */
|
|
19
|
+
readonly maxDepth?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface JsonBoundViolation {
|
|
22
|
+
readonly path: ReadonlyArray<string | number>;
|
|
23
|
+
readonly code: "too_big" | "too_deep" | "cyclic";
|
|
24
|
+
readonly message: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Walks a JSON-like value and returns the first structural bound violation.
|
|
28
|
+
* Returns `null` when the value is within limits.
|
|
29
|
+
*
|
|
30
|
+
* Cyclic object graphs are rejected (`cyclic`) so the walker itself cannot be
|
|
31
|
+
* forced into unbounded recursion. Primitives (including large strings) are
|
|
32
|
+
* accepted by this helper — size must be constrained by callers (e.g. Worker
|
|
33
|
+
* body limits).
|
|
34
|
+
*/
|
|
35
|
+
export declare function findJsonBoundViolation(value: unknown, limits?: JsonBoundLimits): JsonBoundViolation | null;
|
|
36
|
+
//# sourceMappingURL=bounds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,iEAAiE;IACjE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,eAAoB,GAC3B,kBAAkB,GAAG,IAAI,CAc3B"}
|
package/dist/bounds.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural limits applied **before** Zod validation.
|
|
3
|
+
*
|
|
4
|
+
* ContentRail's public schema API targets Zod **4.6.7+** (native `zod` entry),
|
|
5
|
+
* which remediates GHSA-mrhx-c7xc-m3w5. These bounds remain as defense-in-depth
|
|
6
|
+
* at trust boundaries and provide stable ContentRail error codes (`too_big`,
|
|
7
|
+
* `too_deep`, `cyclic`) independent of Zod issue shaping.
|
|
8
|
+
*
|
|
9
|
+
* Limitations (explicit): these checks bound nesting, array length, and object
|
|
10
|
+
* key count. They do **not** cap string byte length, total serialized size, or
|
|
11
|
+
* CPU spent validating values that are within the structural limits.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_MAX_ARRAY_LENGTH = 10_000;
|
|
14
|
+
const DEFAULT_MAX_OBJECT_KEYS = 10_000;
|
|
15
|
+
const DEFAULT_MAX_DEPTH = 32;
|
|
16
|
+
/**
|
|
17
|
+
* Walks a JSON-like value and returns the first structural bound violation.
|
|
18
|
+
* Returns `null` when the value is within limits.
|
|
19
|
+
*
|
|
20
|
+
* Cyclic object graphs are rejected (`cyclic`) so the walker itself cannot be
|
|
21
|
+
* forced into unbounded recursion. Primitives (including large strings) are
|
|
22
|
+
* accepted by this helper — size must be constrained by callers (e.g. Worker
|
|
23
|
+
* body limits).
|
|
24
|
+
*/
|
|
25
|
+
export function findJsonBoundViolation(value, limits = {}) {
|
|
26
|
+
const maxArrayLength = limits.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH;
|
|
27
|
+
const maxObjectKeys = limits.maxObjectKeys ?? DEFAULT_MAX_OBJECT_KEYS;
|
|
28
|
+
const maxDepth = limits.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
29
|
+
return walk(value, [], 0, maxArrayLength, maxObjectKeys, maxDepth, new WeakSet());
|
|
30
|
+
}
|
|
31
|
+
function walk(value, path, depth, maxArrayLength, maxObjectKeys, maxDepth, seen) {
|
|
32
|
+
if (depth > maxDepth) {
|
|
33
|
+
return {
|
|
34
|
+
path,
|
|
35
|
+
code: "too_deep",
|
|
36
|
+
message: `Value exceeds maximum nesting depth of ${maxDepth}.`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (value === null || typeof value !== "object") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (seen.has(value)) {
|
|
43
|
+
return {
|
|
44
|
+
path,
|
|
45
|
+
code: "cyclic",
|
|
46
|
+
message: "Cyclic object graph is not allowed.",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
seen.add(value);
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
if (value.length > maxArrayLength) {
|
|
52
|
+
return {
|
|
53
|
+
path,
|
|
54
|
+
code: "too_big",
|
|
55
|
+
message: `Array length ${value.length} exceeds limit ${maxArrayLength}.`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
59
|
+
const nested = walk(value[index], [...path, index], depth + 1, maxArrayLength, maxObjectKeys, maxDepth, seen);
|
|
60
|
+
if (nested !== null) {
|
|
61
|
+
return nested;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const keys = Object.keys(value);
|
|
67
|
+
if (keys.length > maxObjectKeys) {
|
|
68
|
+
return {
|
|
69
|
+
path,
|
|
70
|
+
code: "too_big",
|
|
71
|
+
message: `Object key count ${keys.length} exceeds limit ${maxObjectKeys}.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const record = value;
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
const nested = walk(record[key], [...path, key], depth + 1, maxArrayLength, maxObjectKeys, maxDepth, seen);
|
|
77
|
+
if (nested !== null) {
|
|
78
|
+
return nested;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=bounds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounds.js","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ContentCollection, ContentSchema, DefineCollectionInput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Defines a content collection bound to a Zod schema.
|
|
4
|
+
* Does not touch storage or publishing — schema and validation only.
|
|
5
|
+
*/
|
|
6
|
+
export declare function defineCollection<TSchema extends ContentSchema>(input: DefineCollectionInput<TSchema>): ContentCollection<TSchema>;
|
|
7
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EAEtB,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,aAAa,EAC5D,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,GACpC,iBAAiB,CAAC,OAAO,CAAC,CA4C5B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ZodObject } from "zod";
|
|
2
|
+
import { ContentDefinitionError } from "./errors.js";
|
|
3
|
+
const COLLECTION_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
4
|
+
/**
|
|
5
|
+
* Defines a content collection bound to a Zod schema.
|
|
6
|
+
* Does not touch storage or publishing — schema and validation only.
|
|
7
|
+
*/
|
|
8
|
+
export function defineCollection(input) {
|
|
9
|
+
if (input === null || typeof input !== "object") {
|
|
10
|
+
throw new ContentDefinitionError("Collection definition must be a non-null object.");
|
|
11
|
+
}
|
|
12
|
+
const name = input.name;
|
|
13
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
14
|
+
throw new ContentDefinitionError('Collection "name" must be a non-empty string.');
|
|
15
|
+
}
|
|
16
|
+
if (!COLLECTION_NAME_PATTERN.test(name)) {
|
|
17
|
+
throw new ContentDefinitionError(`Collection "name" must match ${COLLECTION_NAME_PATTERN}: received "${name}".`);
|
|
18
|
+
}
|
|
19
|
+
if (input.schema === undefined || input.schema === null) {
|
|
20
|
+
throw new ContentDefinitionError(`Collection "${name}" requires a Zod "schema".`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof input.schema.safeParse !== "function") {
|
|
23
|
+
throw new ContentDefinitionError(`Collection "${name}" "schema" must be a Zod schema with safeParse().`);
|
|
24
|
+
}
|
|
25
|
+
const unknownKeys = input.unknownKeys ?? "strip";
|
|
26
|
+
if (unknownKeys !== "strip" && unknownKeys !== "strict") {
|
|
27
|
+
throw new ContentDefinitionError(`Collection "${name}" "unknownKeys" must be "strip" or "strict".`);
|
|
28
|
+
}
|
|
29
|
+
const schema = applyUnknownKeysPolicy(input.schema, unknownKeys);
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
name,
|
|
32
|
+
schema,
|
|
33
|
+
unknownKeys,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function applyUnknownKeysPolicy(schema, policy) {
|
|
37
|
+
if (!(schema instanceof ZodObject)) {
|
|
38
|
+
return schema;
|
|
39
|
+
}
|
|
40
|
+
if (policy === "strict") {
|
|
41
|
+
return schema.strict();
|
|
42
|
+
}
|
|
43
|
+
return schema.strip();
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAQrD,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAqC;IAErC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,sBAAsB,CAC9B,kDAAkD,CACnD,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,sBAAsB,CAC9B,+CAA+C,CAChD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,sBAAsB,CAC9B,gCAAgC,uBAAuB,eAAe,IAAI,IAAI,CAC/E,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACxD,MAAM,IAAI,sBAAsB,CAC9B,eAAe,IAAI,4BAA4B,CAChD,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QACjD,MAAM,IAAI,sBAAsB,CAC9B,eAAe,IAAI,mDAAmD,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAsB,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC;IACpE,IAAI,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,sBAAsB,CAC9B,eAAe,IAAI,8CAA8C,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAY,CAAC;IAE5E,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,IAAI;QACJ,MAAM;QACN,WAAW;KACZ,CAAC,CAAC;AACL,CAAC;AAED,SAAS,sBAAsB,CAC7B,MAAe,EACf,MAAyB;IAEzB,IAAI,CAAC,CAAC,MAAM,YAAY,SAAS,CAAC,EAAE,CAAC;QACnC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single structured validation issue.
|
|
3
|
+
* Paths use Zod-style segments (string keys and numeric array indexes).
|
|
4
|
+
*/
|
|
5
|
+
export interface ValidationIssue {
|
|
6
|
+
readonly path: ReadonlyArray<string | number>;
|
|
7
|
+
readonly code: string;
|
|
8
|
+
readonly message: string;
|
|
9
|
+
readonly expected?: string;
|
|
10
|
+
readonly received?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class ContentDefinitionError extends Error {
|
|
13
|
+
readonly code: "CONTENT_DEFINITION_ERROR";
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export declare class ContentValidationError extends Error {
|
|
17
|
+
readonly code: "CONTENT_VALIDATION_ERROR";
|
|
18
|
+
readonly collection: string;
|
|
19
|
+
readonly issues: readonly ValidationIssue[];
|
|
20
|
+
constructor(collection: string, issues: readonly ValidationIssue[]);
|
|
21
|
+
toJSON(): {
|
|
22
|
+
name: string;
|
|
23
|
+
code: "CONTENT_VALIDATION_ERROR";
|
|
24
|
+
collection: string;
|
|
25
|
+
message: string;
|
|
26
|
+
issues: readonly ValidationIssue[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAG,0BAA0B,CAAU;gBAExC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAG,0BAA0B,CAAU;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;gBAEhC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,eAAe,EAAE;IAWlE,MAAM,IAAI;QACR,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,0BAA0B,CAAC;QACjC,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;KACpC;CASF"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class ContentDefinitionError extends Error {
|
|
2
|
+
code = "CONTENT_DEFINITION_ERROR";
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ContentDefinitionError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class ContentValidationError extends Error {
|
|
9
|
+
code = "CONTENT_VALIDATION_ERROR";
|
|
10
|
+
collection;
|
|
11
|
+
issues;
|
|
12
|
+
constructor(collection, issues) {
|
|
13
|
+
const summary = issues.length === 1
|
|
14
|
+
? formatIssue(issues[0])
|
|
15
|
+
: `${issues.length} validation issues`;
|
|
16
|
+
super(`Invalid content for collection "${collection}": ${summary}`);
|
|
17
|
+
this.name = "ContentValidationError";
|
|
18
|
+
this.collection = collection;
|
|
19
|
+
this.issues = issues;
|
|
20
|
+
}
|
|
21
|
+
toJSON() {
|
|
22
|
+
return {
|
|
23
|
+
name: this.name,
|
|
24
|
+
code: this.code,
|
|
25
|
+
collection: this.collection,
|
|
26
|
+
message: this.message,
|
|
27
|
+
issues: this.issues,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function formatIssue(issue) {
|
|
32
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
33
|
+
return `${path}: ${issue.message}`;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACtC,IAAI,GAAG,0BAAmC,CAAC;IAEpD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACtC,IAAI,GAAG,0BAAmC,CAAC;IAC3C,UAAU,CAAS;IACnB,MAAM,CAA6B;IAE5C,YAAY,UAAkB,EAAE,MAAkC;QAChE,MAAM,OAAO,GACX,MAAM,CAAC,MAAM,KAAK,CAAC;YACjB,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC;YACzB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,oBAAoB,CAAC;QAC3C,KAAK,CAAC,mCAAmC,UAAU,MAAM,OAAO,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,MAAM;QAOJ,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC;IACJ,CAAC;CACF;AAED,SAAS,WAAW,CAAC,KAAsB;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACrE,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;AACrC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { findJsonBoundViolation, type JsonBoundLimits, type JsonBoundViolation, } from "./bounds.js";
|
|
2
|
+
export { defineCollection } from "./collection.js";
|
|
3
|
+
export { ContentDefinitionError, ContentValidationError, type ValidationIssue, } from "./errors.js";
|
|
4
|
+
export type { ContentCollection, ContentSchema, DefineCollectionInput, InferEntry, UnknownKeysPolicy, ValidationFailure, ValidationResult, ValidationSuccess, } from "./types.js";
|
|
5
|
+
export { DEFAULT_MAX_COLLECTION_ENTRIES, assertValidEntry, validateCollection, validateEntry, type ValidateCollectionOptions, type ValidateEntryOptions, } from "./validate.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,8BAA8B,EAC9B,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { findJsonBoundViolation, } from "./bounds.js";
|
|
2
|
+
export { defineCollection } from "./collection.js";
|
|
3
|
+
export { ContentDefinitionError, ContentValidationError, } from "./errors.js";
|
|
4
|
+
export { DEFAULT_MAX_COLLECTION_ENTRIES, assertValidEntry, validateCollection, validateEntry, } from "./validate.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,GAGvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,aAAa,CAAC;AAWrB,OAAO,EACL,8BAA8B,EAC9B,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,GAGd,MAAM,eAAe,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { ContentValidationError } from "./errors.js";
|
|
3
|
+
/** How root object schemas treat keys not declared in the schema. */
|
|
4
|
+
export type UnknownKeysPolicy = "strip" | "strict";
|
|
5
|
+
/** Zod 4 schema constraint used across ContentRail public generics. */
|
|
6
|
+
export type ContentSchema = z.ZodType;
|
|
7
|
+
export interface DefineCollectionInput<TSchema extends ContentSchema> {
|
|
8
|
+
/** Stable collection identifier used in errors and tooling. */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
/** Zod schema describing one content entry. */
|
|
11
|
+
readonly schema: TSchema;
|
|
12
|
+
/**
|
|
13
|
+
* Unexpected root-object field policy.
|
|
14
|
+
* - `strip` (default): remove unknown keys (Zod object default).
|
|
15
|
+
* - `strict`: reject unknown keys.
|
|
16
|
+
*
|
|
17
|
+
* Applies only when `schema` is a Zod object schema (including `.strict()` /
|
|
18
|
+
* `.passthrough()` wrappers replaced according to this policy).
|
|
19
|
+
*/
|
|
20
|
+
readonly unknownKeys?: UnknownKeysPolicy;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A defined content collection. This is a schema-bound definition only —
|
|
24
|
+
* it does not read, write, or publish content.
|
|
25
|
+
*/
|
|
26
|
+
export interface ContentCollection<TSchema extends ContentSchema = ContentSchema> {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly schema: TSchema;
|
|
29
|
+
readonly unknownKeys: UnknownKeysPolicy;
|
|
30
|
+
}
|
|
31
|
+
/** Inferred entry type for a collection. */
|
|
32
|
+
export type InferEntry<TCollection extends ContentCollection> = z.infer<TCollection["schema"]>;
|
|
33
|
+
export type ValidationSuccess<T> = {
|
|
34
|
+
readonly success: true;
|
|
35
|
+
readonly data: T;
|
|
36
|
+
};
|
|
37
|
+
export type ValidationFailure = {
|
|
38
|
+
readonly success: false;
|
|
39
|
+
readonly error: ContentValidationError;
|
|
40
|
+
};
|
|
41
|
+
export type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnD,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,OAAO,CAAC;AAEtC,MAAM,WAAW,qBAAqB,CAAC,OAAO,SAAS,aAAa;IAClE,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;CAC1C;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAChC,OAAO,SAAS,aAAa,GAAG,aAAa;IAE7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;CACzC;AAED,4CAA4C;AAC5C,MAAM,MAAM,UAAU,CAAC,WAAW,SAAS,iBAAiB,IAAI,CAAC,CAAC,KAAK,CACrE,WAAW,CAAC,QAAQ,CAAC,CACtB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type JsonBoundLimits } from "./bounds.js";
|
|
2
|
+
import type { ContentCollection, InferEntry, ValidationResult } from "./types.js";
|
|
3
|
+
/** Default cap for `validateCollection` input length (entries, not nested fields). */
|
|
4
|
+
export declare const DEFAULT_MAX_COLLECTION_ENTRIES = 10000;
|
|
5
|
+
export interface ValidateEntryOptions {
|
|
6
|
+
readonly bounds?: JsonBoundLimits;
|
|
7
|
+
}
|
|
8
|
+
export interface ValidateCollectionOptions extends ValidateEntryOptions {
|
|
9
|
+
/** Maximum number of top-level entries. Default: 10_000. */
|
|
10
|
+
readonly maxEntries?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Validates a single content entry against the collection schema.
|
|
14
|
+
* Never throws for invalid content — returns a structured result.
|
|
15
|
+
*
|
|
16
|
+
* Structural JSON bounds run before Zod as defense-in-depth against oversized
|
|
17
|
+
* nested arrays/objects (GHSA-mrhx-c7xc-m3w5 was fixed in Zod 4.6.6+; bounds
|
|
18
|
+
* remain for defense-in-depth and consistent ContentRail error codes).
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateEntry<TCollection extends ContentCollection>(collection: TCollection, entry: unknown, options?: ValidateEntryOptions): ValidationResult<InferEntry<TCollection>>;
|
|
21
|
+
/**
|
|
22
|
+
* Validates an ordered list of entries for a collection.
|
|
23
|
+
* Aggregates issues from every failing entry; paths are prefixed with the index.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateCollection<TCollection extends ContentCollection>(collection: TCollection, entries: unknown, options?: ValidateCollectionOptions): ValidationResult<Array<InferEntry<TCollection>>>;
|
|
26
|
+
/** Narrow helper for callers that prefer throwing on failure. */
|
|
27
|
+
export declare function assertValidEntry<TCollection extends ContentCollection>(collection: TCollection, entry: unknown, options?: ValidateEntryOptions): InferEntry<TCollection>;
|
|
28
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,sFAAsF;AACtF,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACrE,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,WAAW,SAAS,iBAAiB,EACjE,UAAU,EAAE,WAAW,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,oBAAyB,GACjC,gBAAgB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAgC3C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,SAAS,iBAAiB,EACtE,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,yBAA8B,GACtC,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CA+DlD;AA6ED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,WAAW,SAAS,iBAAiB,EACpE,UAAU,EAAE,WAAW,EACvB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,oBAAyB,GACjC,UAAU,CAAC,WAAW,CAAC,CAMzB"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { findJsonBoundViolation } from "./bounds.js";
|
|
2
|
+
import { ContentValidationError } from "./errors.js";
|
|
3
|
+
/** Default cap for `validateCollection` input length (entries, not nested fields). */
|
|
4
|
+
export const DEFAULT_MAX_COLLECTION_ENTRIES = 10_000;
|
|
5
|
+
/**
|
|
6
|
+
* Validates a single content entry against the collection schema.
|
|
7
|
+
* Never throws for invalid content — returns a structured result.
|
|
8
|
+
*
|
|
9
|
+
* Structural JSON bounds run before Zod as defense-in-depth against oversized
|
|
10
|
+
* nested arrays/objects (GHSA-mrhx-c7xc-m3w5 was fixed in Zod 4.6.6+; bounds
|
|
11
|
+
* remain for defense-in-depth and consistent ContentRail error codes).
|
|
12
|
+
*/
|
|
13
|
+
export function validateEntry(collection, entry, options = {}) {
|
|
14
|
+
assertCollection(collection);
|
|
15
|
+
const bound = findJsonBoundViolation(entry, options.bounds);
|
|
16
|
+
if (bound !== null) {
|
|
17
|
+
return {
|
|
18
|
+
success: false,
|
|
19
|
+
error: new ContentValidationError(collection.name, [
|
|
20
|
+
{
|
|
21
|
+
path: [...bound.path],
|
|
22
|
+
code: bound.code,
|
|
23
|
+
message: bound.message,
|
|
24
|
+
},
|
|
25
|
+
]),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const parsed = collection.schema.safeParse(entry);
|
|
29
|
+
if (parsed.success) {
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
data: parsed.data,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: new ContentValidationError(collection.name, parsed.error.issues.map(toValidationIssue)),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Validates an ordered list of entries for a collection.
|
|
42
|
+
* Aggregates issues from every failing entry; paths are prefixed with the index.
|
|
43
|
+
*/
|
|
44
|
+
export function validateCollection(collection, entries, options = {}) {
|
|
45
|
+
assertCollection(collection);
|
|
46
|
+
if (!Array.isArray(entries)) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: new ContentValidationError(collection.name, [
|
|
50
|
+
{
|
|
51
|
+
path: [],
|
|
52
|
+
code: "invalid_type",
|
|
53
|
+
message: "Collection entries must be an array.",
|
|
54
|
+
expected: "array",
|
|
55
|
+
received: typeName(entries),
|
|
56
|
+
},
|
|
57
|
+
]),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_COLLECTION_ENTRIES;
|
|
61
|
+
if (entries.length > maxEntries) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: new ContentValidationError(collection.name, [
|
|
65
|
+
{
|
|
66
|
+
path: [],
|
|
67
|
+
code: "too_big",
|
|
68
|
+
message: `Collection has ${entries.length} entries; limit is ${maxEntries}.`,
|
|
69
|
+
},
|
|
70
|
+
]),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const data = [];
|
|
74
|
+
const issues = [];
|
|
75
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
76
|
+
const entryOptions = options.bounds === undefined ? {} : { bounds: options.bounds };
|
|
77
|
+
const result = validateEntry(collection, entries[index], entryOptions);
|
|
78
|
+
if (result.success) {
|
|
79
|
+
data.push(result.data);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (const issue of result.error.issues) {
|
|
83
|
+
issues.push({
|
|
84
|
+
path: [index, ...issue.path],
|
|
85
|
+
code: issue.code,
|
|
86
|
+
message: issue.message,
|
|
87
|
+
...(issue.expected !== undefined ? { expected: issue.expected } : {}),
|
|
88
|
+
...(issue.received !== undefined ? { received: issue.received } : {}),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (issues.length > 0) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
error: new ContentValidationError(collection.name, issues),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { success: true, data };
|
|
99
|
+
}
|
|
100
|
+
function assertCollection(collection) {
|
|
101
|
+
if (collection === null ||
|
|
102
|
+
typeof collection !== "object" ||
|
|
103
|
+
typeof collection.name !== "string" ||
|
|
104
|
+
collection.schema === undefined ||
|
|
105
|
+
typeof collection.schema.safeParse !== "function") {
|
|
106
|
+
throw new TypeError("validateEntry/validateCollection require a ContentCollection from defineCollection().");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function toValidationIssue(issue) {
|
|
110
|
+
const record = issue;
|
|
111
|
+
const base = {
|
|
112
|
+
path: issue.path.map((segment) => {
|
|
113
|
+
if (typeof segment === "string" || typeof segment === "number") {
|
|
114
|
+
return segment;
|
|
115
|
+
}
|
|
116
|
+
return String(segment);
|
|
117
|
+
}),
|
|
118
|
+
code: issue.code,
|
|
119
|
+
message: issue.message,
|
|
120
|
+
};
|
|
121
|
+
if (issue.code === "invalid_type") {
|
|
122
|
+
const expected = record.expected;
|
|
123
|
+
const input = record.input;
|
|
124
|
+
const received = record.received;
|
|
125
|
+
return {
|
|
126
|
+
...base,
|
|
127
|
+
...(expected !== undefined ? { expected: String(expected) } : {}),
|
|
128
|
+
...(input !== undefined
|
|
129
|
+
? { received: typeName(input) }
|
|
130
|
+
: received !== undefined
|
|
131
|
+
? { received: String(received) }
|
|
132
|
+
: {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (issue.code === "unrecognized_keys") {
|
|
136
|
+
const keys = Array.isArray(record.keys) ? record.keys.map(String) : [];
|
|
137
|
+
return {
|
|
138
|
+
...base,
|
|
139
|
+
received: keys.join(", "),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (issue.code === "invalid_format") {
|
|
143
|
+
const format = record.format;
|
|
144
|
+
const input = record.input;
|
|
145
|
+
return {
|
|
146
|
+
...base,
|
|
147
|
+
...(format !== undefined ? { expected: String(format) } : {}),
|
|
148
|
+
...(input !== undefined ? { received: String(input) } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return base;
|
|
152
|
+
}
|
|
153
|
+
function typeName(value) {
|
|
154
|
+
if (value === null) {
|
|
155
|
+
return "null";
|
|
156
|
+
}
|
|
157
|
+
if (Array.isArray(value)) {
|
|
158
|
+
return "array";
|
|
159
|
+
}
|
|
160
|
+
return typeof value;
|
|
161
|
+
}
|
|
162
|
+
/** Narrow helper for callers that prefer throwing on failure. */
|
|
163
|
+
export function assertValidEntry(collection, entry, options = {}) {
|
|
164
|
+
const result = validateEntry(collection, entry, options);
|
|
165
|
+
if (!result.success) {
|
|
166
|
+
throw result.error;
|
|
167
|
+
}
|
|
168
|
+
return result.data;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrail/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Framework-independent ContentRail schemas, validation, and content types.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"zod": "^4.6.7"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"zod": "^4.6.7"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.20.5",
|
|
32
|
+
"typescript": "5.9.3",
|
|
33
|
+
"zod": "4.6.7"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/Hridhin-k/contentrail.git",
|
|
38
|
+
"directory": "packages/core"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/Hridhin-k/contentrail/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/Hridhin-k/contentrail#readme",
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.type-tests.json",
|
|
50
|
+
"test": "pnpm run build && tsc -p tsconfig.test.json && node --test test-dist/**/*.test.js"
|
|
51
|
+
}
|
|
6
52
|
}
|