@sleekcms/json-zen 1.0.0 → 1.1.1

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.
Files changed (2) hide show
  1. package/lib/esm/index.js +382 -0
  2. package/package.json +13 -2
@@ -0,0 +1,382 @@
1
+ // src/lib.ts
2
+ function isArray(value) {
3
+ return Array.isArray(value);
4
+ }
5
+ function isObject(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value);
7
+ }
8
+ function isString(value) {
9
+ return typeof value === "string" || value instanceof String;
10
+ }
11
+ function isNumber(value) {
12
+ return typeof value === "number" || value instanceof Number;
13
+ }
14
+ function isBoolean(value) {
15
+ return typeof value === "boolean" || value instanceof Boolean;
16
+ }
17
+ function isRegExp(value) {
18
+ return value instanceof RegExp;
19
+ }
20
+
21
+ // src/types.ts
22
+ var ZenTypes = class _ZenTypes {
23
+ static {
24
+ this.ALIASES = {
25
+ "s": "string",
26
+ "n": "number",
27
+ "b": "boolean"
28
+ };
29
+ }
30
+ static {
31
+ this.TYPES = {
32
+ "string": (v) => v !== void 0 ? isString(v) : "",
33
+ "number": (v) => v !== void 0 ? isNumber(v) : 0,
34
+ "boolean": (v) => v !== void 0 ? isBoolean(v) : true,
35
+ "null": (v) => v !== void 0 ? v === null : null
36
+ };
37
+ }
38
+ constructor(types) {
39
+ this.types = Object.assign({}, _ZenTypes.TYPES);
40
+ if (types) {
41
+ for (const k in types) {
42
+ if (k.match(/^_/)) continue;
43
+ const fn = types[k];
44
+ this.types[k] = _ZenTypes._toType(fn);
45
+ }
46
+ }
47
+ }
48
+ static _toType(fn) {
49
+ if (isArray(fn)) {
50
+ const lookup = fn;
51
+ return (v) => {
52
+ if (v !== void 0) return !isString(v) || lookup.indexOf(v) >= 0;
53
+ else return lookup[Math.random() * lookup.length | 0];
54
+ };
55
+ } else if (isRegExp(fn)) {
56
+ const regex = fn;
57
+ return (v) => {
58
+ if (v !== void 0) return !isString(v) || regex.test(v);
59
+ else return regex.source.replace(/\\(.)/g, "$1");
60
+ };
61
+ }
62
+ return fn;
63
+ }
64
+ static extend(obj) {
65
+ for (const k in obj) {
66
+ this.TYPES[k] = this._toType(obj[k]);
67
+ }
68
+ }
69
+ static toSchema(obj) {
70
+ if (obj === null) return "?";
71
+ if (obj === void 0) return "?";
72
+ if (isArray(obj)) return "array";
73
+ if (isObject(obj)) return "object";
74
+ if (isString(obj)) return "string";
75
+ if (isBoolean(obj)) return "boolean";
76
+ if (isNumber(obj)) return "number";
77
+ return "?";
78
+ }
79
+ resolve(k) {
80
+ return _ZenTypes.ALIASES[k] ?? k;
81
+ }
82
+ sample(k) {
83
+ if (!this.has(k)) return null;
84
+ return this.types[this.resolve(k)]();
85
+ }
86
+ has(k) {
87
+ return !!this.types[this.resolve(k)];
88
+ }
89
+ cast(k, value) {
90
+ const resolved = this.resolve(k);
91
+ if (!this.has(k)) return null;
92
+ if (resolved === "boolean") {
93
+ if (["true", "1", "yes", true].indexOf(value) >= 0) return true;
94
+ return false;
95
+ }
96
+ if (resolved === "number") return parseFloat(value);
97
+ return value;
98
+ }
99
+ check(k, obj, opts) {
100
+ const resolved = this.resolve(k);
101
+ if (!this.has(k)) return false;
102
+ return this.types[resolved](obj, opts);
103
+ }
104
+ show() {
105
+ const obj = {};
106
+ for (const k in this.types) {
107
+ obj[k] = this.types[k]();
108
+ }
109
+ return obj;
110
+ }
111
+ };
112
+ var types_default = ZenTypes;
113
+
114
+ // src/zen-error.ts
115
+ var ZenError = class extends Error {
116
+ constructor(errors) {
117
+ if (errors.length === 1) {
118
+ super(errors[0].join(": "));
119
+ } else {
120
+ super(`Validation failed: ${errors.length} errors found`);
121
+ }
122
+ this.errors = errors;
123
+ }
124
+ };
125
+ var zen_error_default = ZenError;
126
+
127
+ // src/index.ts
128
+ var RX = {
129
+ FLAT_ARRAY: /(\[([^\[\]\{\}]*)\])/,
130
+ FLAT_OBJECT: /(\{([^\{\}\[\]]*)\})/,
131
+ MALFORMED: /[\[\]\{\}]/,
132
+ FLAT_SCALAR: /^[^\[\]\{\}]*$/,
133
+ OPTIONAL: /^[\?]/,
134
+ LOOKUP: /^[0-9]+$/,
135
+ DEFAULTS: /^\$([0-9]+)$/
136
+ };
137
+ var _flatten = (schema) => {
138
+ const lookups = [];
139
+ const default_strings = [];
140
+ let m;
141
+ while (m = schema.match(/"([^"]+)"/)) {
142
+ schema = schema.slice(0, m.index) + `$${default_strings.length}` + schema.slice(m.index + m[0].length);
143
+ default_strings.push(m[1]);
144
+ }
145
+ while (m = schema.match(/'([^']+)'/)) {
146
+ schema = schema.slice(0, m.index) + `$${default_strings.length}` + schema.slice(m.index + m[0].length);
147
+ default_strings.push(m[1]);
148
+ }
149
+ if (schema.match(/"/)) throw new Error("Missing closing quote");
150
+ schema = schema.replace(/\s/g, "");
151
+ function reduce(schema2) {
152
+ let m2;
153
+ let found = false;
154
+ while ((m2 = schema2.match(RX.FLAT_ARRAY)) || (m2 = schema2.match(RX.FLAT_OBJECT))) {
155
+ schema2 = schema2.slice(0, m2.index) + lookups.length + schema2.slice(m2.index + m2[0].length);
156
+ lookups.push(m2[0]);
157
+ found = true;
158
+ }
159
+ if (found) return reduce(schema2);
160
+ if (!found && schema2.match(RX.MALFORMED)) {
161
+ throw new Error("Schema error: probably invalid braces or brackets");
162
+ }
163
+ return schema2;
164
+ }
165
+ schema = reduce(schema);
166
+ return [schema, lookups, default_strings];
167
+ };
168
+ function splitOnce(str, delim) {
169
+ const parts = str.split(delim);
170
+ const first = parts.shift();
171
+ return [first, parts.join(delim)];
172
+ }
173
+ var typeShape = (schema) => {
174
+ let [sch, lookups, default_strings] = _flatten(schema);
175
+ function traverse(sch2) {
176
+ if (!sch2) return "";
177
+ let m;
178
+ if (sch2.match(RX.LOOKUP)) return traverse(lookups[Number(sch2)]);
179
+ if (m = sch2.match(RX.FLAT_ARRAY)) {
180
+ sch2 = m[2];
181
+ const schema_parts = sch2.split(",");
182
+ return schema_parts.length > 0 ? [traverse(schema_parts[0])] : [traverse("")];
183
+ }
184
+ if (m = sch2.match(RX.FLAT_OBJECT)) {
185
+ sch2 = m[2];
186
+ return sch2.split(",").reduce((acc, name) => {
187
+ let [k, v] = splitOnce(name, ":");
188
+ if (k.match(RX.DEFAULTS)) k = default_strings[Number(k.slice(1))];
189
+ acc[k] = traverse(v);
190
+ return acc;
191
+ }, {});
192
+ }
193
+ if (sch2.match(RX.FLAT_SCALAR)) {
194
+ let [type, def] = sch2.split(":");
195
+ if (!def) def = "";
196
+ if (def && def.match(RX.DEFAULTS)) def = default_strings[Number(def.substr(1))];
197
+ let optional = "";
198
+ if (type.match(RX.OPTIONAL)) {
199
+ optional = "?";
200
+ type = type.slice(1);
201
+ }
202
+ const type_lookup = types_default.ALIASES;
203
+ if (type_lookup[type]) type = type_lookup[type];
204
+ return [optional, type, def].join(":");
205
+ }
206
+ return "";
207
+ }
208
+ return traverse(sch);
209
+ };
210
+ var toSchema = (json) => {
211
+ function traverse(obj) {
212
+ const type = types_default.toSchema(obj);
213
+ if (type === "array") {
214
+ const arr = obj;
215
+ const sch = arr.length > 0 ? traverse(arr[0]) : "";
216
+ return `[${sch}]`;
217
+ }
218
+ if (type === "object") {
219
+ const objRecord = obj;
220
+ return "{" + Object.keys(objRecord).map((k) => {
221
+ return `${k}:${traverse(objRecord[k])}`;
222
+ }).join(",") + "}";
223
+ }
224
+ return type;
225
+ }
226
+ return traverse(json);
227
+ };
228
+ var shape = (json, schema, options = {}) => {
229
+ const types = new types_default(options);
230
+ options._optional = options._optional || false;
231
+ let lookups;
232
+ let default_strings;
233
+ [schema, lookups, default_strings] = _flatten(schema);
234
+ const getValue = (type, value, def) => {
235
+ let m;
236
+ if (def && (m = def.match(RX.DEFAULTS))) def = default_strings[Number(m[1])];
237
+ if (types.has(type)) {
238
+ return value && types.check(type, value) ? value : def !== void 0 ? types.cast(type, def) : types.sample(type);
239
+ }
240
+ return value || def || "Not defined!";
241
+ };
242
+ function traverse({ value, schema: schema2 }) {
243
+ if (!schema2) schema2 = "";
244
+ let m;
245
+ let optional = false;
246
+ if (schema2.match(RX.OPTIONAL)) {
247
+ schema2 = schema2.slice(1);
248
+ optional = true;
249
+ }
250
+ if (schema2.match(RX.LOOKUP)) return traverse({ value, schema: `${optional ? "?" : ""}${lookups[Number(schema2)]}` });
251
+ if ((value === null || value === void 0) && optional && !options._optional) return null;
252
+ if (schema2.match(RX.FLAT_SCALAR)) {
253
+ let type;
254
+ let def;
255
+ [type, def] = schema2.split(":");
256
+ return getValue(type, value, def);
257
+ } else if (m = schema2.match(RX.FLAT_ARRAY)) {
258
+ schema2 = m[2];
259
+ const schema_parts = schema2.split(",");
260
+ let arr = value;
261
+ if (!Array.isArray(value)) arr = schema_parts.map(() => null);
262
+ if (arr.length < schema_parts.length) {
263
+ arr = arr.concat(schema_parts.slice(arr.length).map(() => null));
264
+ }
265
+ return arr.map((v, i) => traverse({ value: v, schema: schema_parts[i % schema_parts.length] }));
266
+ } else if (m = schema2.match(RX.FLAT_OBJECT)) {
267
+ let obj = value;
268
+ if (typeof value !== "object" || Array.isArray(value)) {
269
+ obj = {};
270
+ }
271
+ schema2 = m[2];
272
+ if (schema2 === "") return obj;
273
+ let wildcard = false;
274
+ const shp = schema2.split(",").reduce((acc, name) => {
275
+ let [k, t, d] = name.split(":");
276
+ if (!t) t = "";
277
+ if (d) t += ":" + d;
278
+ if (k === "*") wildcard = t;
279
+ else acc[k] = traverse({ value: obj[k], schema: t });
280
+ return acc;
281
+ }, {});
282
+ if (wildcard !== false) {
283
+ for (const k in obj) if (shp[k] === void 0) shp[k] = traverse({ value: obj[k], schema: wildcard });
284
+ }
285
+ return shp;
286
+ }
287
+ return value;
288
+ }
289
+ const result = traverse({ value: json, schema });
290
+ return result;
291
+ };
292
+ var verify = (json, schema, options = {}) => {
293
+ const types = new types_default(options);
294
+ const errors = [];
295
+ const jsonPath = options._path || "json";
296
+ let lookups;
297
+ [schema, lookups] = _flatten(schema);
298
+ function validate({ path, value, schema: schema2, parent = null }) {
299
+ let m;
300
+ let optional = false;
301
+ if (schema2.match(RX.OPTIONAL)) {
302
+ schema2 = schema2.slice(1);
303
+ if (value === void 0 || value === null) return true;
304
+ optional = true;
305
+ }
306
+ if (schema2.match(RX.LOOKUP)) return validate({ path: `${path}`, value, schema: `${optional ? "?" : ""}${lookups[Number(schema2)]}`, parent });
307
+ if (schema2.match(RX.FLAT_SCALAR)) {
308
+ let def;
309
+ [schema2, def] = schema2.split(":");
310
+ if (value === void 0 || value === null) {
311
+ errors.push([path, "is required"]);
312
+ return false;
313
+ }
314
+ if (schema2 === "") return true;
315
+ if (!types.has(schema2)) throw new Error(`Schema error: Validator - ${schema2} - not found`);
316
+ else if (types.check(schema2, value, { path, json, parent })) return true;
317
+ else {
318
+ errors.push([path, "validation failed"]);
319
+ return false;
320
+ }
321
+ } else if (m = schema2.match(RX.FLAT_ARRAY)) {
322
+ if (!Array.isArray(value)) {
323
+ errors.push([path, "should be array"]);
324
+ return false;
325
+ }
326
+ schema2 = m[2];
327
+ const schema_parts = schema2.split(",");
328
+ for (const i in value) {
329
+ validate({ path: `${path}.${i}`, value: value[i], schema: schema_parts[Number(i) % schema_parts.length], parent: value });
330
+ }
331
+ return true;
332
+ } else if (m = schema2.match(RX.FLAT_OBJECT)) {
333
+ if (typeof value !== "object" || Array.isArray(value)) {
334
+ errors.push([path, "should be object"]);
335
+ return false;
336
+ }
337
+ schema2 = m[2];
338
+ let wildcard = false;
339
+ if (schema2 !== "") {
340
+ const obj = value;
341
+ const keys = schema2.split(",").reduce((acc, name) => {
342
+ let [k, t] = name.split(":");
343
+ if (!t) t = "";
344
+ if (k === "*") wildcard = t;
345
+ else acc[k] = t;
346
+ return acc;
347
+ }, {});
348
+ if (wildcard !== false) {
349
+ for (const k in obj) if (keys[k] === void 0) keys[k] = wildcard;
350
+ }
351
+ for (const k in keys) validate({ path: `${path}.${k}`, value: obj[k], schema: keys[k], parent: value });
352
+ }
353
+ return true;
354
+ }
355
+ return true;
356
+ }
357
+ validate({ path: jsonPath, value: json, schema });
358
+ if (errors.length > 0) throw new zen_error_default(errors);
359
+ return true;
360
+ };
361
+ var check = (json, schema, options) => {
362
+ try {
363
+ verify(json, schema, options);
364
+ return true;
365
+ } catch (error) {
366
+ if (error instanceof Error && error.message.match(/^Schema error/)) throw error;
367
+ return false;
368
+ }
369
+ };
370
+ var extendTypes = (types) => {
371
+ types_default.extend(types);
372
+ };
373
+ module.exports = { verify, check, shape, toSchema, _flatten, typeShape, extendTypes };
374
+ export {
375
+ _flatten,
376
+ check,
377
+ extendTypes,
378
+ shape,
379
+ toSchema,
380
+ typeShape,
381
+ verify
382
+ };
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "@sleekcms/json-zen",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Easify verify and shape JSON objects using a simplified alternative JSON schema",
5
5
  "main": "./lib/index.js",
6
+ "module": "./lib/esm/index.js",
6
7
  "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "import": "./lib/esm/index.js",
12
+ "require": "./lib/index.js"
13
+ }
14
+ },
7
15
  "scripts": {
8
16
  "clean": "rimraf lib",
9
17
  "test": "npm run cover",
@@ -11,7 +19,9 @@
11
19
  "test:watch": "npm test -- --watch",
12
20
  "cover": "nyc npm run test:only",
13
21
  "lint": "eslint src test --ext .ts",
14
- "build": "tsc",
22
+ "build:cjs": "tsc",
23
+ "build:esm": "esbuild src/index.ts --bundle --format=esm --platform=browser --outfile=lib/esm/index.js",
24
+ "build": "npm run build:cjs && npm run build:esm",
15
25
  "prepublish": "npm run clean && npm run test && npm run build"
16
26
  },
17
27
  "files": [
@@ -45,6 +55,7 @@
45
55
  "@typescript-eslint/eslint-plugin": "^5.0.0",
46
56
  "@typescript-eslint/parser": "^5.0.0",
47
57
  "chai": "^4.3.4",
58
+ "esbuild": "^0.27.5",
48
59
  "eslint": "^8.3.0",
49
60
  "mocha": "^9.1.3",
50
61
  "nyc": "^15.1.0",