@sleekcms/json-zen 1.4.0 → 1.5.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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A compact, string-based schema syntax for validating and shaping JSON objects — with far less boilerplate than JSON Schema.
4
4
 
5
+ Try it live at **[json-zen.sleekcms.site](https://json-zen.sleekcms.site/)**.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -79,11 +81,12 @@ verify([1, 'x', 2, 'y'], '[n,s]'); // true — alternating number/string
79
81
 
80
82
  ### Optional fields (`?`)
81
83
 
82
- Prefix a type with `?` to make it optional. A missing or `null` value passes validation.
84
+ For object properties, suffix the key with `?` to make it optional (TypeScript-style). For array elements, prefix the type with `?`. A missing or `null` value passes validation.
83
85
 
84
86
  ```js
85
- verify({ a: 1 }, '{a:n, b:?s}'); // true — b is missing but optional
86
- verify({ a: 1, b: null }, '{a:n, b:?s}'); // true
87
+ verify({ a: 1 }, '{a:n, b?:s}'); // true — b is missing but optional
88
+ verify({ a: 1, b: null }, '{a:n, b?:s}'); // true
89
+ verify([1, null, 3], '[?n]'); // true — array elements are optional
87
90
  ```
88
91
 
89
92
  ### Presence-only validation
@@ -198,12 +201,12 @@ shape({ a: {} }, '{a:{x:n, y:s}}');
198
201
 
199
202
  | Option | Type | Description |
200
203
  |-------------|-----------|--------------------------------------------------------------|
201
- | `_optional` | `boolean` | When `true`, fill optional (`?`) fields with defaults instead of `null` |
204
+ | `fillNulls` | `boolean` | When `true`, fill optional (`?`) fields with defaults instead of `null` |
202
205
  | _(key)_ | validator | Inline custom type |
203
206
 
204
207
  ```js
205
- shape({}, '{a:?n}'); // { a: null }
206
- shape({}, '{a:?n}', { _optional: true }); // { a: 0 }
208
+ shape({}, '{a?:n}'); // { a: null }
209
+ shape({}, '{a?:n}', { fillNulls: true }); // { a: 0 }
207
210
  ```
208
211
 
209
212
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/json-zen",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Easify verify and shape JSON objects using a simplified alternative JSON schema",
5
5
  "main": "./lib/index.js",
6
6
  "module": "./lib/esm/index.js",
package/src/index.ts CHANGED
@@ -82,6 +82,11 @@ export const typeShape = (schema: string): TypeShape => {
82
82
  return sch.split(",").reduce((acc: { [key: string]: TypeShape }, name) => {
83
83
  let [k, v] = splitOnce(name, ":");
84
84
  if (k.match(RX.DEFAULTS)) k = default_strings[Number(k.slice(1))];
85
+ // TypeScript-style optional: key? instead of ?type
86
+ if (k.endsWith('?')) {
87
+ k = k.slice(0, -1);
88
+ v = '?' + v;
89
+ }
85
90
  acc[k] = traverse(v);
86
91
  return acc;
87
92
  }, {});
@@ -136,14 +141,14 @@ export const toSchema = (json: unknown, options: ToSchemaOptions = {}): string =
136
141
  };
137
142
 
138
143
  export interface ShapeOptions {
139
- _optional?: boolean;
144
+ fillNulls?: boolean;
140
145
  [key: string]: unknown;
141
146
  }
142
147
 
143
148
  export const shape = <T = unknown>(json: unknown, schema: string, options: ShapeOptions = {}): T => {
144
149
  const types = new ZenTypes(options);
145
150
 
146
- options._optional = options._optional || false;
151
+ options.fillNulls = options.fillNulls || false;
147
152
  let lookups: string[];
148
153
  let default_strings: string[];
149
154
  [schema, lookups, default_strings] = _flatten(schema);
@@ -177,7 +182,7 @@ export const shape = <T = unknown>(json: unknown, schema: string, options: Shape
177
182
  // if lookup, validate further
178
183
  if (schema.match(RX.LOOKUP)) return traverse({ value, schema: `${optional ? '?' : ''}${lookups[Number(schema)]}` });
179
184
 
180
- if ((value === null || value === undefined) && optional && !options._optional) return null;
185
+ if ((value === null || value === undefined) && optional && !options.fillNulls) return null;
181
186
 
182
187
  if (schema.match(RX.FLAT_SCALAR)) {
183
188
  // if scalar
@@ -212,6 +217,11 @@ export const shape = <T = unknown>(json: unknown, schema: string, options: Shape
212
217
  let [k, t, d] = name.split(":");
213
218
  if (!t) t = "";
214
219
  if (d) t += ":" + d;
220
+ // TypeScript-style optional: key? instead of ?type
221
+ if (k.endsWith('?')) {
222
+ k = k.slice(0, -1);
223
+ t = '?' + t;
224
+ }
215
225
  if (k === "*") wildcard = t;
216
226
  else acc[k] = traverse({ value: obj[k], schema: t });
217
227
  return acc;
@@ -304,6 +314,11 @@ export const verify = (json: unknown, schema: string, options: VerifyOptions = {
304
314
  const keys = schema.split(",").reduce((acc: Record<string, string>, name) => {
305
315
  let [k, t] = name.split(":");
306
316
  if (!t) t = "";
317
+ // TypeScript-style optional: key? instead of ?type
318
+ if (k.endsWith('?')) {
319
+ k = k.slice(0, -1);
320
+ t = '?' + t;
321
+ }
307
322
  if (k === '*') wildcard = t;
308
323
  else acc[k] = t;
309
324
  return acc;