inferred-types 0.54.7 → 0.54.9

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
@@ -70,6 +70,20 @@ const people = pluralize("person");
70
70
  #### Data Patterns
71
71
 
72
72
  - types like `Url`, `IpAddress`, `CSV`, `DotPath`, `Hexadecimal`, `ZipCode`, and `DomainName` attempt to provide an out of the box type for common data structure patterns we find in the real world
73
+ - ISO3166 support (aka, countries):
74
+ - `Iso3166_Alpha2`, `Iso3166_Alpha3`, ...
75
+ - `isIso3166Alpha2()`, `isIsoAlpha3()`, type guards ...
76
+ - ISO8601 support (aka, datetime)
77
+ - `Iso8601DateTime`, `Iso8601Date`, `Iso8601Time`, ...
78
+ - `isIsoDateTime()`, `isIsoDate()`, `isIsoTime()`, ...
79
+
80
+ #### String Literal Matching at design time and run time
81
+
82
+ ```ts
83
+ const matcher = infer("{{ string }} is a {{ infer foo }} utility, that {{ infer bar }}");
84
+ // { foo: "fancy"; bar: "thinks it's better than you!"}
85
+ const fooBar = matcher("infer is a fancy utility, that thinks it's better than you!")
86
+ ```
73
87
 
74
88
  ### Numeric Literals
75
89
 
@@ -89,42 +103,73 @@ const people = pluralize("person");
89
103
 
90
104
  ### Object / Dictionaries
91
105
 
92
- 1. Based on an object's entity values:
93
-
94
- Assume a base type of `Obj`:
95
-
96
- ```ts
97
- type Obj = {
98
- n1: number;
99
- n2: 2;
100
- n3: 3;
101
- success: true;
102
- s1: string;
103
- s2: "hello";
104
- }
105
- ```
106
-
107
- We can get a union of string literals representing the _keys_ on the object whose value _extends_ some value:
108
-
109
- ```ts
110
- import type { KeysWithValue, KeysWithoutValue } from "inferred-types";
111
- // "s1" | "s2"
112
- type S = KeysWithValue<Obj, string>;
113
- // "success" | "n1" | "n2"
114
- type N = KeysWithoutValue<Obj, string>;
115
- ```
116
-
117
- > though less used, you can also use `KeysEqualValue` and `KeysNotEqualValue` for equality matching
118
-
119
- If you'd prefer to mutate the object's type rather than just identify the _keys_ which extend a value you can do this with: `WithValue` and `WithoutValue`:
120
-
121
- ```ts
122
- import type { WithValue, WithoutValue } from "inferred-types";
123
- // { s1: string; s2: "hello" }
124
- type S = WithValue<Obj, string>;
125
- // { success: true; n1: number; n2: 2; n3: 3 }
126
- type N = WithoutValue<Obj, string>;
127
- ```
106
+ #### Reduce Object to Keys with a knownValue
107
+
108
+ Assume a base type of `Obj`:
109
+
110
+ ```ts
111
+ type Obj = {
112
+ n1: number;
113
+ n2: 2;
114
+ n3: 3;
115
+ success: true;
116
+ s1: string;
117
+ s2: "hello";
118
+ }
119
+ ```
120
+
121
+ We can get a union of string literals representing the _keys_ on the object whose value _extends_ some value:
122
+
123
+ ```ts
124
+ import type { KeysWithValue, KeysWithoutValue } from "inferred-types";
125
+ // "s1" | "s2"
126
+ type S = KeysWithValue<Obj, string>;
127
+ // "success" | "n1" | "n2"
128
+ type N = KeysWithoutValue<Obj, string>;
129
+ ```
130
+
131
+ > though less used, you can also use `KeysEqualValue` and `KeysNotEqualValue` for equality matching
132
+
133
+ If you'd prefer to mutate the object's type rather than just identify the _keys_ which extend a value you can do this with: `WithValue` and `WithoutValue`:
134
+
135
+ ```ts
136
+ import type { WithValue, WithoutValue } from "inferred-types";
137
+ // { s1: string; s2: "hello" }
138
+ type S = WithValue<Obj, string>;
139
+ // { success: true; n1: number; n2: 2; n3: 3 }
140
+ type N = WithoutValue<Obj, string>;
141
+ ```
142
+
143
+ And at runtime:
144
+
145
+ ```ts
146
+ // { foo: "hi" }
147
+ const foo = withoutValue("number")({ foo: "hi", bar: 42, baz: 99 });
148
+ // { bar: 42 }
149
+ const bar = withoutValue("number(42,55,66)")({ foo: "hi", bar: 42, baz: 99 });
150
+
151
+ // { foo: "hi", bar: 42 }
152
+ const fooBar = withoutKeys("baz")({ foo: "hi", bar: 42, baz: 99 });
153
+ // { foo: "hi", bar: 42 }
154
+ const fooBar2 = withKeys("foo", "bar")({ foo: "hi", bar: 42, baz: 99 })
155
+ ```
156
+
157
+ #### Reduce Object's to only `Required` or `Optional` keys
158
+
159
+ ```ts
160
+ type Obj = { foo: string; bar?: string; baz?: number };
161
+ // "foo"
162
+ type ReqKeys = RequiredKeys<Obj>;
163
+ // ["foo"]
164
+ type ReqKeyTup = RequiredKeysTuple<Obj>;
165
+ // "bar" | "baz"
166
+ type OptKeys = OptionalKeys<Obj>;
167
+ // ["bar", "baz"]
168
+ type OptKeyTup = OptionalKeysTuple<Obj>;
169
+
170
+ type Reduced =
171
+
172
+ ```
128
173
 
129
174
  ## Contributing
130
175
 
@@ -430,12 +430,14 @@ __export(src_exports, {
430
430
  isLuminosityUom: () => isLuminosityUom,
431
431
  isLuxonDateTime: () => isLuxonDateTime,
432
432
  isMacysUrl: () => isMacysUrl,
433
+ isMap: () => isMap,
433
434
  isMapToken: () => isMapToken,
434
435
  isMassMetric: () => isMassMetric,
435
436
  isMassUom: () => isMassUom,
436
437
  isMetric: () => isMetric,
437
438
  isMexicanNewsUrl: () => isMexicanNewsUrl,
438
439
  isMoment: () => isMoment,
440
+ isNarrowableObject: () => isNarrowableObject,
439
441
  isNever: () => isNever,
440
442
  isNewsUrl: () => isNewsUrl,
441
443
  isNikeUrl: () => isNikeUrl,
@@ -470,6 +472,7 @@ __export(src_exports, {
470
472
  isSet: () => isSet,
471
473
  isSetBasedKind: () => isSetBasedKind,
472
474
  isSetBasedToken: () => isSetBasedToken,
475
+ isSetContainer: () => isSetContainer,
473
476
  isSetToken: () => isSetToken,
474
477
  isShape: () => isShape,
475
478
  isShapeCallback: () => isShapeCallback,
@@ -579,6 +582,7 @@ __export(src_exports, {
579
582
  narrowObjectToType: () => narrowObjectToType,
580
583
  never: () => never,
581
584
  objectToApi: () => objectToApi,
585
+ objectValues: () => objectValues,
582
586
  omit: () => omit,
583
587
  optional: () => optional,
584
588
  optionalOrNull: () => optionalOrNull,
@@ -655,6 +659,7 @@ __export(src_exports, {
655
659
  widen: () => widen,
656
660
  withDefaults: () => withDefaults,
657
661
  withKeys: () => withKeys,
662
+ withValue: () => withValue,
658
663
  withoutKeys: () => withoutKeys,
659
664
  withoutValue: () => withoutValue,
660
665
  wrapFn: () => wrapFn,
@@ -5258,6 +5263,16 @@ function narrowObjectToType() {
5258
5263
  }
5259
5264
  );
5260
5265
  }
5266
+ function objectValues(obj) {
5267
+ const tuple3 = Object.keys(obj).reduce(
5268
+ (acc, key) => [
5269
+ ...acc,
5270
+ obj[key]
5271
+ ],
5272
+ []
5273
+ );
5274
+ return tuple3;
5275
+ }
5261
5276
  function omit(obj, ...removeKeys) {
5262
5277
  const keys = Object.keys(obj);
5263
5278
  return keys.reduce(
@@ -5308,12 +5323,110 @@ function withKeys(dict, ...keys) {
5308
5323
  function withoutKeys(dict, ...exclude) {
5309
5324
  return omit(dict, ...exclude);
5310
5325
  }
5311
- function withoutValue(val) {
5326
+ function doesExtend(type) {
5327
+ return (val) => {
5328
+ let response = false;
5329
+ if (isString(val)) {
5330
+ if (type === "string") {
5331
+ response = true;
5332
+ }
5333
+ if (type.startsWith("string(")) {
5334
+ const literals = stripAfter(
5335
+ stripBefore(type, "string("),
5336
+ ")"
5337
+ ).split(/,\s*/);
5338
+ if (literals.includes(val)) {
5339
+ response = true;
5340
+ }
5341
+ }
5342
+ }
5343
+ if (isNumber(val)) {
5344
+ if (type === "number") {
5345
+ response = true;
5346
+ }
5347
+ if (type.startsWith("number(")) {
5348
+ const literals = stripAfter(
5349
+ stripBefore(type, "number("),
5350
+ ")"
5351
+ ).split(/,\s*/).map(Number);
5352
+ if (literals.includes(val)) {
5353
+ response = true;
5354
+ }
5355
+ }
5356
+ }
5357
+ if (isNull(val) && (type === "null" || type === "Opt<null>")) {
5358
+ response = true;
5359
+ }
5360
+ if (isUndefined(val) && (type === "undefined" || type.startsWith("Opt<"))) {
5361
+ response = true;
5362
+ }
5363
+ if (isBoolean(val)) {
5364
+ if (type === "boolean") {
5365
+ response = true;
5366
+ }
5367
+ if (type === "true" && val === true || type === "false" && val === false) {
5368
+ response = true;
5369
+ }
5370
+ }
5371
+ if (isNarrowableObject(val)) {
5372
+ if (type === "Dict" || type === "Dict<string, unknown>") {
5373
+ response = true;
5374
+ }
5375
+ if (startsWith("Dict<")(type)) {
5376
+ const match = infer("Dict<{{infer key}}, {{infer value}}>")(type);
5377
+ if (match) {
5378
+ const { value } = match;
5379
+ const isOpt = value.includes(`Opt<`);
5380
+ const values = objectValues(val);
5381
+ if (values.every((i) => isOpt ? isString(i) || isUndefined(i) : isString(i)) && type === "Dict<string, string>" || values.every((i) => isOpt ? isUndefined(i) || isNumber(i) : isNumber(i)) && type === "Dict<string, number>" || values.every((i) => isOpt ? isUndefined(i) || isBoolean(i) : isBoolean(i)) && type === "Dict<string, boolean>") {
5382
+ response = true;
5383
+ }
5384
+ }
5385
+ }
5386
+ }
5387
+ if (isArray(val)) {
5388
+ if (type === "Array" || type === "Array<unknown>") {
5389
+ return true;
5390
+ }
5391
+ if (type === "Array<Dict>" && val.every(isObject) || type === "Array<boolean>" && val.every(isBoolean) || type === "Array<string>" && val.every(isString) || type === "Array<number>" && val.every(isNumber) || type === "Array<Map>" && val.every(isMap) || type === "Array<Set>" && val.every(isSetContainer)) {
5392
+ response = true;
5393
+ }
5394
+ }
5395
+ if (isMap(val)) {
5396
+ if (type === "Map" || type === "Map<Dict, Array>" && Array.from(val.keys()).every(isObject) && Array.from(val.values()).every(isArray) || type === "Map<Dict, Dict>" && Array.from(val.keys()).every(isObject) && Array.from(val.values()).every(isObject) || type === "Map<Dict, boolean>" && Array.from(val.keys()).every(isObject) && Array.from(val.values()).every(isBoolean) || type === "Map<Dict, number>" && Array.from(val.keys()).every(isObject) && Array.from(val.values()).every(isNumber) || type === "Map<Dict, string>" && Array.from(val.keys()).every(isObject) && Array.from(val.values()).every(isString) || type === "Map<Dict, unknown>" && Array.from(val.keys()).every(isObject) || type === "Map<string, string>" && Array.from(val.keys()).every(isString) && Array.from(val.values()).every(isString) || type === "Map<string, number>" && Array.from(val.keys()).every(isString) && Array.from(val.values()).every(isNumber) || type === "Map<string, boolean>" && Array.from(val.keys()).every(isString) && Array.from(val.values()).every(isBoolean) || type === "Map<string, unknown>" && Array.from(val.keys()).every(isString) || type === "Map<number, string>" && Array.from(val.keys()).every(isNumber) && Array.from(val.values()).every(isString) || type === "Map<number, number>" && Array.from(val.keys()).every(isNumber) && Array.from(val.values()).every(isNumber) || type === "Map<number, boolean>" && Array.from(val.keys()).every(isNumber) && Array.from(val.values()).every(isBoolean) || type === "Map<number, unknown>" && Array.from(val.keys()).every(isNumber)) {
5397
+ response = true;
5398
+ }
5399
+ }
5400
+ if (isSetContainer(val)) {
5401
+ if (type === "Set" || type === "Set<unknown>" || type === "Set<boolean>" && Array.from(val).every(isBoolean) || type === "Set<string>" && Array.from(val).every(isString) || type === "Set<number>" && Array.from(val).every(isNumber) || type === "Set<Opt<boolean>>" && Array.from(val).every((i) => isBoolean(i) || isUndefined(i)) || type === "Set<Opt<string>>" && Array.from(val).every((i) => isString(i) || isUndefined(i)) || type === "Set<Opt<number>>" && Array.from(val).every((i) => isNumber(i) || isUndefined(i))) {
5402
+ response = true;
5403
+ }
5404
+ }
5405
+ return response;
5406
+ };
5407
+ }
5408
+ function withoutValue(wo) {
5312
5409
  return (obj) => {
5313
- return Object.keys(obj).reduce(
5314
- (acc, key) => val === obj[key] ? acc : { ...acc, [key]: obj[key] },
5315
- {}
5316
- );
5410
+ const output = {};
5411
+ for (const key of keysOf(obj)) {
5412
+ const val = obj[key];
5413
+ if (!doesExtend(wo)(val)) {
5414
+ output[key] = val;
5415
+ }
5416
+ }
5417
+ return output;
5418
+ };
5419
+ }
5420
+ function withValue(wo) {
5421
+ return (obj) => {
5422
+ const output = {};
5423
+ for (const key of keysOf(obj)) {
5424
+ const val = obj[key];
5425
+ if (doesExtend(wo)(val)) {
5426
+ output[key] = val;
5427
+ }
5428
+ }
5429
+ return output;
5317
5430
  };
5318
5431
  }
5319
5432
  function createErrorCondition(kind, msg = "", utility = "") {
@@ -5532,6 +5645,9 @@ function isFunction(value) {
5532
5645
  function isObject(value) {
5533
5646
  return typeof value === "object" && value !== null && Array.isArray(value) === false;
5534
5647
  }
5648
+ function isNarrowableObject(value) {
5649
+ return isObject(value) && Object.keys(value).every((key) => ["string", "number", "boolean", "symbol", "object", "undefined", "void", "null"].includes(typeof value[key]));
5650
+ }
5535
5651
  function isEscapeFunction(val) {
5536
5652
  return isFunction(val) && "escape" in val && val.escape === true;
5537
5653
  }
@@ -6088,6 +6204,9 @@ function isIndexable(value) {
6088
6204
  function isInlineSvg(v) {
6089
6205
  return isString(v) && v.trim().startsWith(`<svg`) && v.trim().endsWith(`</svg>`);
6090
6206
  }
6207
+ function isMap(val) {
6208
+ return val instanceof Map;
6209
+ }
6091
6210
  function isNever(val) {
6092
6211
  return isConstant(val) && val.kind === "never";
6093
6212
  }
@@ -6136,6 +6255,12 @@ function isLikeRegExp(val) {
6136
6255
  }
6137
6256
  return false;
6138
6257
  }
6258
+ function isSet(val) {
6259
+ return isObject(val) ? val.kind !== "Unset" : true;
6260
+ }
6261
+ function isSetContainer(val) {
6262
+ return val instanceof Set;
6263
+ }
6139
6264
  function isStringArray(val) {
6140
6265
  return Array.isArray(val) && val.every((i) => isString(i));
6141
6266
  }
@@ -6160,9 +6285,6 @@ function isTypeTuple(value) {
6160
6285
  function isUnset(val) {
6161
6286
  return isObject(val) && val.kind === "Unset";
6162
6287
  }
6163
- function isSet(val) {
6164
- return isObject(val) ? val.kind !== "Unset" : true;
6165
- }
6166
6288
  function isUri(val, ...protocols) {
6167
6289
  const p = protocols.length === 0 ? valuesOf(NETWORK_PROTOCOL_LOOKUP2).flat().filter((i) => i) : protocols;
6168
6290
  return isString(val) && p.some((i) => val.startsWith(`${i}://`));
@@ -6742,26 +6864,23 @@ function ifUppercaseChar(ch, callbackForMatch, callbackForNoMatch) {
6742
6864
  return LOWER_ALPHA_CHARS2.includes(ch) ? callbackForMatch(ch) : callbackForNoMatch(ch);
6743
6865
  }
6744
6866
  function parseTemplate(template) {
6745
- const pattern = /\{\{\s*infer\s+([A-Za-z_]\w*)\s*(?:as\s+(string|number|boolean)\s*)?\}\}/g;
6867
+ const pattern = /\{\{\s*infer\s+([A-Za-z_]\w*)\s*(?:(?:extends|as)\s+(string|number|boolean)\s*)?\}\}/g;
6746
6868
  let lastIndex = 0;
6747
6869
  let match;
6748
6870
  const segments = [];
6749
- do {
6750
- match = pattern.exec(template);
6751
- if (match) {
6752
- const [fullMatch, varName, asType2] = match;
6753
- const staticPart = template.slice(lastIndex, match.index);
6754
- if (staticPart) {
6755
- segments.push({ dynamic: false, text: staticPart });
6756
- }
6757
- segments.push({
6758
- dynamic: true,
6759
- varName,
6760
- type: asType2 ? asType2 : "string"
6761
- });
6762
- lastIndex = match.index + fullMatch.length;
6871
+ while (match = pattern.exec(template)) {
6872
+ const [fullMatch, varName, asType2] = match;
6873
+ const staticPart = template.slice(lastIndex, match.index);
6874
+ if (staticPart) {
6875
+ segments.push({ dynamic: false, text: staticPart });
6763
6876
  }
6764
- } while (match);
6877
+ segments.push({
6878
+ dynamic: true,
6879
+ varName,
6880
+ type: asType2 ? asType2 : "string"
6881
+ });
6882
+ lastIndex = match.index + fullMatch.length;
6883
+ }
6765
6884
  const remainder = template.slice(lastIndex);
6766
6885
  if (remainder) {
6767
6886
  segments.push({ dynamic: false, text: remainder });
@@ -6779,19 +6898,19 @@ function buildRegexPattern(segments) {
6779
6898
  } else {
6780
6899
  switch (seg.type) {
6781
6900
  case "string":
6782
- regexStr += `(?<${seg.varName}>.*?)`;
6901
+ regexStr += "(.*?)";
6783
6902
  break;
6784
6903
  case "number":
6785
- regexStr += `(?<${seg.varName}>\\d+)`;
6904
+ regexStr += "(\\d+)";
6786
6905
  break;
6787
6906
  case "boolean":
6788
- regexStr += `(?<${seg.varName}>(?:true|false))`;
6907
+ regexStr += "(true|false)";
6789
6908
  break;
6790
6909
  }
6791
6910
  }
6792
6911
  }
6793
6912
  regexStr += "$";
6794
- return new RegExp(regexStr, "s");
6913
+ return new RegExp(regexStr);
6795
6914
  }
6796
6915
  function convertValue(type, value) {
6797
6916
  switch (type) {
@@ -6803,19 +6922,20 @@ function convertValue(type, value) {
6803
6922
  return value === "true";
6804
6923
  }
6805
6924
  }
6806
- function matchTemplate(inference, test) {
6807
- const segments = parseTemplate(inference);
6925
+ function matchTemplate(template, test) {
6926
+ const segments = parseTemplate(template);
6808
6927
  const regex = buildRegexPattern(segments);
6809
- const match = test.match(regex);
6928
+ const match = regex.exec(test);
6810
6929
  if (!match)
6811
6930
  return false;
6931
+ let captureIndex = 1;
6812
6932
  const result2 = {};
6813
6933
  for (const seg of segments) {
6814
6934
  if (seg.dynamic) {
6815
- const rawVal = match.groups?.[seg.varName];
6935
+ const rawVal = match[captureIndex++];
6816
6936
  if (rawVal === void 0)
6817
6937
  return false;
6818
- result2[seg.varName.toLowerCase()] = convertValue(seg.type, rawVal);
6938
+ result2[seg.varName] = convertValue(seg.type, rawVal);
6819
6939
  }
6820
6940
  }
6821
6941
  return result2;
@@ -8145,12 +8265,14 @@ var ExifSaturation = /* @__PURE__ */ ((ExifSaturation2) => {
8145
8265
  isLuminosityUom,
8146
8266
  isLuxonDateTime,
8147
8267
  isMacysUrl,
8268
+ isMap,
8148
8269
  isMapToken,
8149
8270
  isMassMetric,
8150
8271
  isMassUom,
8151
8272
  isMetric,
8152
8273
  isMexicanNewsUrl,
8153
8274
  isMoment,
8275
+ isNarrowableObject,
8154
8276
  isNever,
8155
8277
  isNewsUrl,
8156
8278
  isNikeUrl,
@@ -8185,6 +8307,7 @@ var ExifSaturation = /* @__PURE__ */ ((ExifSaturation2) => {
8185
8307
  isSet,
8186
8308
  isSetBasedKind,
8187
8309
  isSetBasedToken,
8310
+ isSetContainer,
8188
8311
  isSetToken,
8189
8312
  isShape,
8190
8313
  isShapeCallback,
@@ -8294,6 +8417,7 @@ var ExifSaturation = /* @__PURE__ */ ((ExifSaturation2) => {
8294
8417
  narrowObjectToType,
8295
8418
  never,
8296
8419
  objectToApi,
8420
+ objectValues,
8297
8421
  omit,
8298
8422
  optional,
8299
8423
  optionalOrNull,
@@ -8370,6 +8494,7 @@ var ExifSaturation = /* @__PURE__ */ ((ExifSaturation2) => {
8370
8494
  widen,
8371
8495
  withDefaults,
8372
8496
  withKeys,
8497
+ withValue,
8373
8498
  withoutKeys,
8374
8499
  withoutValue,
8375
8500
  wrapFn,