@amritk/generate-parsers 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -75,7 +75,7 @@ Write them to disk however you like.
75
75
 
76
76
  ## API
77
77
 
78
- ### `buildSchema(rootSchema, rootTypeName, extensions?, typesOnly?, logWarnings?)`
78
+ ### `buildSchema(rootSchema, rootTypeName, extensions?, typesOnly?, logWarnings?, strict?)`
79
79
 
80
80
  | Parameter | Type | Description |
81
81
  |:---|:---|:---|
@@ -84,6 +84,7 @@ Write them to disk however you like.
84
84
  | `extensions` | `SchemaExtensions` _(optional)_ | Map of definition name → extra optional properties to merge in before generation. |
85
85
  | `typesOnly` | `boolean` _(optional)_ | When `true`, only emit `.ts` type definitions — skip parser functions and runtime helpers. |
86
86
  | `logWarnings` | `boolean` _(optional)_ | When `true`, generated parsers emit a `console.warn` for every input key not declared in the schema's properties. |
87
+ | `strict` | `boolean` _(optional)_ | When `true`, generated parsers throw on type/shape mismatches (wrong type, missing required property, enum/pattern/min/max violations) instead of coercing invalid input to default values. |
87
88
 
88
89
  Returns: `Promise<GeneratedFile[]>`.
89
90
 
@@ -96,6 +97,7 @@ Returns: `Promise<GeneratedFile[]>`.
96
97
  |:---:|:---|:---|:---:|:---:|:---:|:---|
97
98
  | 🏷️ | `typesOnly` | — | `boolean` | — | `false` | Generate only TypeScript type definitions without parser functions. Runtime helper files (validators, isObject) are also omitted since they are only needed for parsers. |
98
99
  | ⚠️ | `logWarnings` | — | `boolean` | — | `false` | Emit a console.warn in the generated parsers for every input key that is not declared in the schema's properties. Useful for detecting schema drift or unexpected data shapes at runtime. |
100
+ | 🚫 | `strict` | — | `boolean` | — | `false` | Generate parsers that throw on type/shape mismatches (wrong type, missing required property, enum/pattern/min/max violations) instead of coercing invalid input to default values. Unknown extra keys are still allowed. |
99
101
  <!-- config-table-end -->
100
102
 
101
103
  The generator handles:
@@ -27,6 +27,9 @@ export type GeneratedFile = {
27
27
  * @param typesOnly - When true, only generate TypeScript type definitions without parser functions.
28
28
  * @param logWarnings - When true, the generated parsers emit a console.warn for every input key
29
29
  * that is not declared in the schema's properties.
30
+ * @param strict - When true, the generated parsers throw on type/shape mismatches
31
+ * (wrong type, missing required property, enum/pattern/min/max violations) instead
32
+ * of coercing invalid input to default values.
30
33
  * @returns An array of generated TypeScript files
31
34
  *
32
35
  * @example
@@ -59,4 +62,4 @@ export type GeneratedFile = {
59
62
  * });
60
63
  * ```
61
64
  */
62
- export declare const buildSchema: (rootSchema: JSONSchema, rootTypeName: string, extensions?: SchemaExtensions, typesOnly?: boolean, logWarnings?: boolean) => Promise<GeneratedFile[]>;
65
+ export declare const buildSchema: (rootSchema: JSONSchema, rootTypeName: string, extensions?: SchemaExtensions, typesOnly?: boolean, logWarnings?: boolean, strict?: boolean) => Promise<GeneratedFile[]>;
@@ -24,6 +24,11 @@ type GenerateFileOptions = {
24
24
  * that is not declared in the schema's properties.
25
25
  */
26
26
  readonly logWarnings?: boolean;
27
+ /**
28
+ * When true, the generated parser throws on type/shape mismatches instead
29
+ * of coercing invalid input to default values.
30
+ */
31
+ readonly strict?: boolean;
27
32
  };
28
33
  /**
29
34
  * Generates a complete TypeScript file from a JSON Schema.
@@ -14,6 +14,12 @@ type GenerateParserOptions = {
14
14
  * that is not declared in the schema's properties.
15
15
  */
16
16
  readonly logWarnings?: boolean;
17
+ /**
18
+ * When true, the generated parser throws on type/shape mismatches instead
19
+ * of coercing invalid input to default values. Throws on the first violation
20
+ * with a path-aware Error. Unknown extra keys are still allowed.
21
+ */
22
+ readonly strict?: boolean;
17
23
  };
18
24
  /**
19
25
  * Generates a safe parser as an arrow function that never throws.
@@ -0,0 +1,18 @@
1
+ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
2
+ /**
3
+ * Generates strict-mode assertions for the body of an object parser.
4
+ * Throws on:
5
+ * - non-object input
6
+ * - missing required property
7
+ * - property of the wrong primitive type
8
+ * - enum / pattern / length / min / max / multipleOf violations
9
+ *
10
+ * Properties with a `$ref` are validated by the nested parser's own strict
11
+ * check when that parser is invoked downstream.
12
+ */
13
+ export declare const generateObjectStrictAssertion: (schema: JSONSchema, typeName: string) => string[];
14
+ /**
15
+ * Generates a single strict-mode assertion line for a non-object scalar parser.
16
+ * Returns null when the schema has no type information to assert on.
17
+ */
18
+ export declare const generateScalarStrictAssertion: (schema: JSONSchema, typeName: string) => string | null;
package/dist/index.js CHANGED
@@ -1142,6 +1142,119 @@ var getDefaultValue = (schema) => {
1142
1142
  }
1143
1143
  };
1144
1144
 
1145
+ // src/generators/generate-strict-assertion.ts
1146
+ var wrongTypeCondition = (accessor, type) => {
1147
+ switch (type) {
1148
+ case "string":
1149
+ return `typeof ${accessor} !== "string"`;
1150
+ case "number":
1151
+ case "integer":
1152
+ return `typeof ${accessor} !== "number"`;
1153
+ case "boolean":
1154
+ return `typeof ${accessor} !== "boolean"`;
1155
+ case "array":
1156
+ return `!Array.isArray(${accessor})`;
1157
+ case "object":
1158
+ return `!isObject(${accessor})`;
1159
+ default:
1160
+ return null;
1161
+ }
1162
+ };
1163
+ var typeLabel = (type) => type === "integer" ? "number" : type;
1164
+ var escapeRegexPattern = (pattern) => {
1165
+ return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
1166
+ };
1167
+ var generateConstraintChecks = (acc, propSchema, typeName, key) => {
1168
+ if (!isSchemaObject3(propSchema) || !hasType(propSchema))
1169
+ return [];
1170
+ const t = propSchema.type;
1171
+ const lines = [];
1172
+ if (t === "string") {
1173
+ if (hasPattern(propSchema)) {
1174
+ const pattern = escapeRegexPattern(propSchema.pattern);
1175
+ lines.push(` if (typeof ${acc} === "string" && !/${pattern}/.test(${acc})) throw new Error('[${typeName}] field "${key}" must match pattern ${propSchema.pattern}');`);
1176
+ }
1177
+ if (hasMinLength(propSchema)) {
1178
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length < ${propSchema.minLength}) throw new Error('[${typeName}] field "${key}" must have at least ${propSchema.minLength} characters');`);
1179
+ }
1180
+ if (hasMaxLength(propSchema)) {
1181
+ lines.push(` if (typeof ${acc} === "string" && ${acc}.length > ${propSchema.maxLength}) throw new Error('[${typeName}] field "${key}" must have at most ${propSchema.maxLength} characters');`);
1182
+ }
1183
+ }
1184
+ if (t === "number" || t === "integer") {
1185
+ if (hasMinimum(propSchema)) {
1186
+ lines.push(` if (typeof ${acc} === "number" && ${acc} < ${propSchema.minimum}) throw new Error('[${typeName}] field "${key}" must be >= ${propSchema.minimum}');`);
1187
+ }
1188
+ if (hasMaximum(propSchema)) {
1189
+ lines.push(` if (typeof ${acc} === "number" && ${acc} > ${propSchema.maximum}) throw new Error('[${typeName}] field "${key}" must be <= ${propSchema.maximum}');`);
1190
+ }
1191
+ if (hasExclusiveMinimum(propSchema)) {
1192
+ lines.push(` if (typeof ${acc} === "number" && ${acc} <= ${propSchema.exclusiveMinimum}) throw new Error('[${typeName}] field "${key}" must be > ${propSchema.exclusiveMinimum}');`);
1193
+ }
1194
+ if (hasExclusiveMaximum(propSchema)) {
1195
+ lines.push(` if (typeof ${acc} === "number" && ${acc} >= ${propSchema.exclusiveMaximum}) throw new Error('[${typeName}] field "${key}" must be < ${propSchema.exclusiveMaximum}');`);
1196
+ }
1197
+ if (hasMultipleOf(propSchema)) {
1198
+ lines.push(` if (typeof ${acc} === "number" && ${acc} % ${propSchema.multipleOf} !== 0) throw new Error('[${typeName}] field "${key}" must be a multiple of ${propSchema.multipleOf}');`);
1199
+ }
1200
+ }
1201
+ return lines;
1202
+ };
1203
+ var generatePropertyAssertion = (key, propSchema, isRequired, typeName) => {
1204
+ const acc = safeAccessor("input", key);
1205
+ const lines = [];
1206
+ if (isRequired) {
1207
+ lines.push(` if (!(${JSON.stringify(key)} in input)) throw new Error('[${typeName}] missing required property "${key}"');`);
1208
+ }
1209
+ if (!isSchemaObject3(propSchema))
1210
+ return lines;
1211
+ if (hasRef(propSchema))
1212
+ return lines;
1213
+ if (hasEnum(propSchema)) {
1214
+ const allowed = JSON.stringify(propSchema.enum);
1215
+ const label = propSchema.enum.map((v) => JSON.stringify(v)).join(", ");
1216
+ if (isRequired) {
1217
+ lines.push(` if (!(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
1218
+ } else {
1219
+ lines.push(` if (${acc} !== undefined && !(${allowed} as readonly unknown[]).includes(${acc})) throw new Error('[${typeName}] field "${key}" must be one of: ${label}');`);
1220
+ }
1221
+ return lines;
1222
+ }
1223
+ if (hasType(propSchema)) {
1224
+ const t = propSchema.type;
1225
+ const wrongType = wrongTypeCondition(acc, t);
1226
+ if (wrongType) {
1227
+ if (isRequired) {
1228
+ lines.push(` if (${wrongType}) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
1229
+ } else {
1230
+ lines.push(` if (${acc} !== undefined && (${wrongType})) throw new Error(\`[${typeName}] field "${key}" expected ${typeLabel(t)}, got \${typeof ${acc}}\`);`);
1231
+ }
1232
+ }
1233
+ lines.push(...generateConstraintChecks(acc, propSchema, typeName, key));
1234
+ }
1235
+ return lines;
1236
+ };
1237
+ var generateObjectStrictAssertion = (schema, typeName) => {
1238
+ const lines = [];
1239
+ lines.push(` if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);`);
1240
+ if (!hasProperties(schema) || !isSchemaObject3(schema))
1241
+ return lines;
1242
+ const required = new Set(hasRequired(schema) ? schema.required : []);
1243
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
1244
+ lines.push(...generatePropertyAssertion(key, propSchema, required.has(key), typeName));
1245
+ }
1246
+ return lines;
1247
+ };
1248
+ var generateScalarStrictAssertion = (schema, typeName) => {
1249
+ if (!isSchemaObject3(schema) || !hasType(schema))
1250
+ return null;
1251
+ const t = schema.type;
1252
+ const wrongType = wrongTypeCondition("input", t);
1253
+ if (!wrongType)
1254
+ return null;
1255
+ return ` if (${wrongType}) throw new Error(\`[${typeName}] expected ${typeLabel(t)}, got \${input === null ? "null" : typeof input}\`);`;
1256
+ };
1257
+
1145
1258
  // src/helpers/find-discriminator.ts
1146
1259
  var findDiscriminator = (schemas) => {
1147
1260
  const propertyValues = new Map;
@@ -1712,11 +1825,22 @@ var generateFallbackObject = (schema, useRefImports, typeName) => {
1712
1825
  }`;
1713
1826
  return result;
1714
1827
  };
1715
- var generateNonObjectParser = (typeName, schema) => {
1828
+ var generateNonObjectParser = (typeName, schema, strict) => {
1716
1829
  const functionName = generateParserName(typeName);
1717
1830
  if (!isSchemaObject3(schema) || !hasType(schema)) {
1718
1831
  return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1719
1832
  }
1833
+ if (strict) {
1834
+ const assertion = generateScalarStrictAssertion(schema, typeName);
1835
+ if (assertion === null) {
1836
+ return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1837
+ }
1838
+ const returnExpr = schema.type === "array" ? `[...(input as readonly unknown[])] as ${typeName}` : `input as ${typeName}`;
1839
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1840
+ ${assertion}
1841
+ return ${returnExpr};
1842
+ };`;
1843
+ }
1720
1844
  switch (schema.type) {
1721
1845
  case "string":
1722
1846
  return `export const ${functionName} = (input: unknown): ${typeName} => typeof input === "string" ? input as ${typeName} : "" as ${typeName};`;
@@ -1731,8 +1855,14 @@ var generateNonObjectParser = (typeName, schema) => {
1731
1855
  return `export const ${functionName} = (input: unknown): ${typeName} => input as ${typeName};`;
1732
1856
  }
1733
1857
  };
1734
- var generateEmptyObjectParser = (typeName) => {
1858
+ var generateEmptyObjectParser = (typeName, strict) => {
1735
1859
  const functionName = generateParserName(typeName);
1860
+ if (strict) {
1861
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1862
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
1863
+ return { ...input } as ${typeName};
1864
+ };`;
1865
+ }
1736
1866
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {} as ${typeName};`;
1737
1867
  };
1738
1868
  var toVarName = (key) => {
@@ -1761,7 +1891,7 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
1761
1891
  case "string": {
1762
1892
  checks.push(`typeof ${varName} === "string"`);
1763
1893
  if (hasPattern(schema))
1764
- checks.push(`/${escapeRegexPattern(schema.pattern)}/.test(${varName})`);
1894
+ checks.push(`/${escapeRegexPattern2(schema.pattern)}/.test(${varName})`);
1765
1895
  if (hasMinLength(schema))
1766
1896
  checks.push(`${varName}.length >= ${schema.minLength}`);
1767
1897
  if (hasMaxLength(schema))
@@ -1814,16 +1944,28 @@ var generatePropertyTypeCheck = (varName, schema, useRefImports) => {
1814
1944
  var shouldCacheVariable = (propSchema, _canFastPath, _useRefImports) => {
1815
1945
  return isSchemaObject3(propSchema);
1816
1946
  };
1817
- var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
1947
+ var generateObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
1818
1948
  const functionName = generateParserName(typeName);
1819
1949
  if (!hasProperties(schema)) {
1950
+ if (strict) {
1951
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
1952
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
1953
+ return input as ${typeName};
1954
+ };`;
1955
+ }
1820
1956
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? input as ${typeName} : {} as ${typeName};`;
1821
1957
  }
1822
1958
  const fallbackObject = generateFallbackObject(schema, useRefImports, typeName);
1823
1959
  const properties = Object.entries(schema.properties);
1824
1960
  const lines = [];
1825
1961
  lines.push(`export const ${functionName} = (input: unknown): ${typeName} => {`);
1826
- lines.push(` if (!isObject(input)) return ${fallbackObject};`);
1962
+ if (strict) {
1963
+ for (const assertionLine of generateObjectStrictAssertion(schema, typeName)) {
1964
+ lines.push(assertionLine);
1965
+ }
1966
+ } else {
1967
+ lines.push(` if (!isObject(input)) return ${fallbackObject};`);
1968
+ }
1827
1969
  const propInfo = [];
1828
1970
  const hasAllOfRefParsers = useRefImports && isSchemaObject3(schema) && hasAllOf(schema) && schema.allOf.some((entry) => isSchemaObject3(entry) && hasRef(entry));
1829
1971
  let canFastPath = !hasAllOfRefParsers;
@@ -1946,7 +2088,7 @@ var generateObjectParser = (schema, typeName, useRefImports, logWarnings) => {
1946
2088
  }
1947
2089
  return result;
1948
2090
  };
1949
- var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings) => {
2091
+ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings, strict) => {
1950
2092
  const functionName = generateParserName(typeName);
1951
2093
  const entries = generatePropertyEntries(schema, useRefImports);
1952
2094
  const propertyLines = entries.map((entry) => {
@@ -1956,19 +2098,19 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
1956
2098
  return ` ${safeKey2(entry.key)}: ${entry.value},`;
1957
2099
  });
1958
2100
  if (!isSchemaObject3(schema) || !("patternProperties" in schema)) {
1959
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2101
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
1960
2102
  }
1961
2103
  const patternProps = schema.patternProperties;
1962
2104
  const patterns = Object.entries(patternProps);
1963
2105
  const refPattern = patterns.find(([, ps]) => isSchemaObject3(ps) && hasRef(ps));
1964
2106
  if (!refPattern || !useRefImports) {
1965
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2107
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
1966
2108
  }
1967
2109
  const [pattern, patternSchema] = refPattern;
1968
2110
  const ref = patternSchema.$ref;
1969
2111
  const parserName = generateParserName(refToName(ref));
1970
2112
  const assignmentCode = `(result as Record<string, unknown>)[key] = ${parserName}(value);`;
1971
- const escapedPattern = escapeRegexPattern(pattern);
2113
+ const escapedPattern = escapeRegexPattern2(pattern);
1972
2114
  const inputSpread = " ...input,";
1973
2115
  let objectProperties = inputSpread;
1974
2116
  if (propertyLines.length > 0) {
@@ -1977,9 +2119,10 @@ var generateCombinedObjectParser = (schema, typeName, useRefImports, logWarnings
1977
2119
  ` + line;
1978
2120
  }
1979
2121
  }
2122
+ const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
1980
2123
  return `export const ${functionName} = (input: unknown): ${typeName} => {
1981
2124
  if (!isObject(input)) {
1982
- return {} as unknown as ${typeName};
2125
+ ${notObjectBranch}
1983
2126
  }
1984
2127
  const result = {
1985
2128
  ${objectProperties}
@@ -1993,23 +2136,41 @@ ${objectProperties}
1993
2136
  return result;
1994
2137
  };`;
1995
2138
  };
1996
- var generateAdditionalPropertiesParser = (schema, typeName, useRefImports) => {
2139
+ var generateAdditionalPropertiesParser = (schema, typeName, useRefImports, strict) => {
1997
2140
  const functionName = generateParserName(typeName);
1998
2141
  const additionalProps = schema.additionalProperties;
1999
2142
  if (!additionalProps) {
2000
- return generateEmptyObjectParser(typeName);
2143
+ return generateEmptyObjectParser(typeName, strict);
2001
2144
  }
2002
2145
  if (useRefImports && isSchemaObject3(additionalProps) && hasRef(additionalProps)) {
2003
2146
  const ref = additionalProps.$ref;
2004
2147
  const parserName = generateParserName(refToName(ref));
2148
+ if (strict) {
2149
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2150
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2151
+ return validateRecord(input, ${parserName}) as ${typeName};
2152
+ };`;
2153
+ }
2005
2154
  return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${parserName}) as ${typeName};`;
2006
2155
  }
2007
2156
  if (isSchemaObject3(additionalProps) && hasType(additionalProps)) {
2008
2157
  const inlineParser = generateInlineValueParser(additionalProps);
2009
2158
  if (inlineParser) {
2159
+ if (strict) {
2160
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2161
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2162
+ return validateRecord(input, ${inlineParser}) as ${typeName};
2163
+ };`;
2164
+ }
2010
2165
  return `export const ${functionName} = (input: unknown): ${typeName} => validateRecord(input, ${inlineParser}) as ${typeName};`;
2011
2166
  }
2012
2167
  }
2168
+ if (strict) {
2169
+ return `export const ${functionName} = (input: unknown): ${typeName} => {
2170
+ if (!isObject(input)) throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);
2171
+ return { ...input } as ${typeName};
2172
+ };`;
2173
+ }
2013
2174
  return `export const ${functionName} = (input: unknown): ${typeName} => isObject(input) ? { ...input } as ${typeName} : {};`;
2014
2175
  };
2015
2176
  var generateInlineValueParser = (schema) => {
@@ -2030,18 +2191,18 @@ var generateInlineValueParser = (schema) => {
2030
2191
  return null;
2031
2192
  }
2032
2193
  };
2033
- var escapeRegexPattern = (pattern) => {
2194
+ var escapeRegexPattern2 = (pattern) => {
2034
2195
  return pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
2035
2196
  };
2036
- var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
2197
+ var generatePatternPropertiesParser = (schema, typeName, useRefImports, strict) => {
2037
2198
  const functionName = generateParserName(typeName);
2038
2199
  if (!("patternProperties" in schema) || typeof schema.patternProperties !== "object") {
2039
- return generateEmptyObjectParser(typeName);
2200
+ return generateEmptyObjectParser(typeName, strict);
2040
2201
  }
2041
2202
  const patternProps = schema.patternProperties;
2042
2203
  const patterns = Object.entries(patternProps);
2043
2204
  if (patterns.length === 0) {
2044
- return generateEmptyObjectParser(typeName);
2205
+ return generateEmptyObjectParser(typeName, strict);
2045
2206
  }
2046
2207
  const [pattern, patternSchema] = patterns[0];
2047
2208
  let patternAssignment = "(result as Record<string, unknown>)[key] = value;";
@@ -2052,10 +2213,11 @@ var generatePatternPropertiesParser = (schema, typeName, useRefImports) => {
2052
2213
  } else if (patternSchema === false) {
2053
2214
  patternAssignment = "";
2054
2215
  }
2055
- const escapedPattern = escapeRegexPattern(pattern);
2216
+ const escapedPattern = escapeRegexPattern2(pattern);
2217
+ const notObjectBranch = strict ? ` throw new Error(\`[${typeName}] expected object, got \${input === null ? "null" : typeof input}\`);` : ` return {} as unknown as ${typeName};`;
2056
2218
  return `export const ${functionName} = (input: unknown): ${typeName} => {
2057
2219
  if (!isObject(input)) {
2058
- return {} as unknown as ${typeName};
2220
+ ${notObjectBranch}
2059
2221
  }
2060
2222
  const result = {
2061
2223
  ...input,
@@ -2167,43 +2329,44 @@ var generateSchemaObjectParser = (typeName) => {
2167
2329
  var selectParserStrategy = (schema, typeName, options) => {
2168
2330
  const useRefImports = options?.useRefImports ?? false;
2169
2331
  const logWarnings = options?.logWarnings ?? false;
2332
+ const strict = options?.strict ?? false;
2170
2333
  if (typeName === "SchemaObject") {
2171
2334
  return generateSchemaObjectParser(typeName);
2172
2335
  }
2173
2336
  const isObjectLikeSchema2 = isObjectSchema2(schema) || isSchemaObject3(schema) && (("patternProperties" in schema) || ("additionalProperties" in schema));
2174
2337
  if (!isObjectLikeSchema2 && !isSchemaObject3(schema)) {
2175
- return generateNonObjectParser(typeName, schema);
2338
+ return generateNonObjectParser(typeName, schema, strict);
2176
2339
  }
2177
2340
  if (hasProperties(schema) && isSchemaObject3(schema) && "patternProperties" in schema) {
2178
- return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings);
2341
+ return generateCombinedObjectParser(schema, typeName, useRefImports, logWarnings, strict);
2179
2342
  }
2180
2343
  if (hasProperties(schema)) {
2181
- return generateObjectParser(schema, typeName, useRefImports, logWarnings);
2344
+ return generateObjectParser(schema, typeName, useRefImports, logWarnings, strict);
2182
2345
  }
2183
2346
  if (isSchemaObject3(schema) && "if" in schema && "then" in schema && "else" in schema) {
2184
2347
  return generateConditionalParser(schema, typeName);
2185
2348
  }
2186
2349
  const conditionalObjectSchema = getConditionalObjectSchema2(schema);
2187
2350
  if (conditionalObjectSchema) {
2188
- return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings);
2351
+ return generateObjectParser(conditionalObjectSchema, typeName, useRefImports, logWarnings, strict);
2189
2352
  }
2190
2353
  if (!isObjectLikeSchema2) {
2191
- return generateNonObjectParser(typeName, schema);
2354
+ return generateNonObjectParser(typeName, schema, strict);
2192
2355
  }
2193
2356
  if ("patternProperties" in schema) {
2194
- return generatePatternPropertiesParser(schema, typeName, useRefImports);
2357
+ return generatePatternPropertiesParser(schema, typeName, useRefImports, strict);
2195
2358
  }
2196
2359
  if ("additionalProperties" in schema && !hasProperties(schema)) {
2197
2360
  const additionalProps = schema.additionalProperties;
2198
2361
  if (additionalProps === true || additionalProps === false) {
2199
- return generateEmptyObjectParser(typeName);
2362
+ return generateEmptyObjectParser(typeName, strict);
2200
2363
  }
2201
- return generateAdditionalPropertiesParser(schema, typeName, useRefImports);
2364
+ return generateAdditionalPropertiesParser(schema, typeName, useRefImports, strict);
2202
2365
  }
2203
2366
  if ("type" in schema && schema.type === "object" && !hasProperties(schema)) {
2204
- return generateEmptyObjectParser(typeName);
2367
+ return generateEmptyObjectParser(typeName, strict);
2205
2368
  }
2206
- return generateEmptyObjectParser(typeName);
2369
+ return generateEmptyObjectParser(typeName, strict);
2207
2370
  };
2208
2371
  var generateParserFunction = (schema, typeName, options) => {
2209
2372
  return selectParserStrategy(schema, typeName, options);
@@ -2273,7 +2436,8 @@ var generateFile = (schema, typeName, options) => {
2273
2436
  }
2274
2437
  const parserFunction = generateParserFunction(schema, typeName, {
2275
2438
  useRefImports: true,
2276
- ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {}
2439
+ ...options?.logWarnings !== undefined ? { logWarnings: options.logWarnings } : {},
2440
+ ...options?.strict !== undefined ? { strict: options.strict } : {}
2277
2441
  });
2278
2442
  const shapeValidator = generateShapeValidator(schema, typeName, true);
2279
2443
  const combinedFunctions = `${shapeValidator}
@@ -2316,7 +2480,7 @@ var extractDynamicAnchorDefs = (schema) => {
2316
2480
  }
2317
2481
  return refs;
2318
2482
  };
2319
- var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings) => {
2483
+ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWarnings, strict) => {
2320
2484
  rootSchema = upgradeDraft07Schema(rootSchema);
2321
2485
  const files = [];
2322
2486
  const processedRefs = new Set;
@@ -2328,7 +2492,8 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
2328
2492
  const rootContent = generateFile(extendedRootSchema, rootTypeName, {
2329
2493
  typesOnly: typesOnly ?? false,
2330
2494
  rootSchema,
2331
- ...logWarnings !== undefined ? { logWarnings } : {}
2495
+ ...logWarnings !== undefined ? { logWarnings } : {},
2496
+ ...strict !== undefined ? { strict } : {}
2332
2497
  });
2333
2498
  const rootFilename = rootTypeName.toLowerCase();
2334
2499
  processedFilenames.add(rootFilename);
@@ -2359,7 +2524,8 @@ var buildSchema = async (rootSchema, rootTypeName, extensions, typesOnly, logWar
2359
2524
  typesOnly: typesOnly ?? false,
2360
2525
  selfRef: ref,
2361
2526
  rootSchema,
2362
- ...logWarnings !== undefined ? { logWarnings } : {}
2527
+ ...logWarnings !== undefined ? { logWarnings } : {},
2528
+ ...strict !== undefined ? { strict } : {}
2363
2529
  });
2364
2530
  if (!processedFilenames.has(filename)) {
2365
2531
  processedFilenames.add(filename);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/generate-parsers",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -25,6 +25,9 @@
25
25
  "files": [
26
26
  "dist"
27
27
  ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
28
31
  "scripts": {
29
32
  "build": "bun run build:code && bun run build:types",
30
33
  "build:code": "bun build ./src/index.ts --outdir=dist --target=node",