@zipbul/baker 3.4.0 → 4.0.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/CHANGELOG.md +46 -0
- package/README.md +236 -148
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -10
- package/dist/src/baker.d.ts +26 -0
- package/dist/src/baker.js +1 -0
- package/dist/src/collect.js +1 -26
- package/dist/src/configure.d.ts +7 -1
- package/dist/src/configure.js +1 -43
- package/dist/src/create-rule.d.ts +2 -1
- package/dist/src/create-rule.js +1 -41
- package/dist/src/decorators/field.d.ts +2 -1
- package/dist/src/decorators/field.js +1 -277
- package/dist/src/decorators/index.js +1 -2
- package/dist/src/decorators/recipe.js +1 -23
- package/dist/src/enums.d.ts +51 -0
- package/dist/src/enums.js +1 -0
- package/dist/src/errors.js +1 -52
- package/dist/src/functions/check-call-options.js +1 -51
- package/dist/src/functions/deserialize.js +1 -57
- package/dist/src/functions/serialize.js +1 -52
- package/dist/src/functions/validate.js +1 -49
- package/dist/src/interfaces.js +0 -4
- package/dist/src/meta-access.js +1 -75
- package/dist/src/registry.js +1 -8
- package/dist/src/rule-metadata.js +1 -17
- package/dist/src/rule-plan.d.ts +5 -3
- package/dist/src/rule-plan.js +1 -117
- package/dist/src/rules/array.js +1 -96
- package/dist/src/rules/binary.js +3 -51
- package/dist/src/rules/combinators.js +1 -111
- package/dist/src/rules/common.js +1 -77
- package/dist/src/rules/date.js +1 -35
- package/dist/src/rules/index.js +1 -10
- package/dist/src/rules/locales.js +1 -249
- package/dist/src/rules/number.js +1 -79
- package/dist/src/rules/object.js +1 -49
- package/dist/src/rules/string.js +10 -2033
- package/dist/src/rules/typechecker.js +5 -171
- package/dist/src/seal/circular-analyzer.js +1 -63
- package/dist/src/seal/codegen-utils.js +1 -18
- package/dist/src/seal/deserialize-builder.js +265 -1564
- package/dist/src/seal/enums.d.ts +8 -0
- package/dist/src/seal/enums.js +1 -0
- package/dist/src/seal/expose-validator.js +1 -65
- package/dist/src/seal/seal-state.js +1 -18
- package/dist/src/seal/seal.d.ts +15 -1
- package/dist/src/seal/seal.js +1 -431
- package/dist/src/seal/serialize-builder.js +66 -370
- package/dist/src/seal/validate-meta.js +1 -61
- package/dist/src/symbols.js +1 -13
- package/dist/src/transformers/collection.transformer.js +1 -25
- package/dist/src/transformers/date.transformer.js +1 -18
- package/dist/src/transformers/index.js +1 -6
- package/dist/src/transformers/luxon.transformer.js +1 -34
- package/dist/src/transformers/moment.transformer.js +1 -32
- package/dist/src/transformers/number.transformer.js +1 -8
- package/dist/src/transformers/string.transformer.js +1 -12
- package/dist/src/types.d.ts +11 -10
- package/dist/src/types.js +0 -1
- package/dist/src/utils.js +1 -10
- package/package.json +2 -2
|
@@ -1,171 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const
|
|
6
|
-
name: 'isString',
|
|
7
|
-
constraints: {},
|
|
8
|
-
validate: value => typeof value === 'string',
|
|
9
|
-
emit: (varName, ctx) => `if (typeof ${varName} !== 'string') ${ctx.fail('isString')};`,
|
|
10
|
-
});
|
|
11
|
-
export function isNumber(options) {
|
|
12
|
-
const allowNaN = options?.allowNaN ?? false;
|
|
13
|
-
const allowInfinity = options?.allowInfinity ?? false;
|
|
14
|
-
const maxDecimalPlaces = options?.maxDecimalPlaces;
|
|
15
|
-
const validate = (value) => {
|
|
16
|
-
if (typeof value !== 'number') {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
// Check NaN first — since isFinite(NaN) is also false, order matters
|
|
20
|
-
if (isNaN(value)) {
|
|
21
|
-
return allowNaN;
|
|
22
|
-
}
|
|
23
|
-
// Non-NaN non-finite values (Infinity / -Infinity)
|
|
24
|
-
if (!isFinite(value)) {
|
|
25
|
-
return allowInfinity;
|
|
26
|
-
}
|
|
27
|
-
if (maxDecimalPlaces !== undefined) {
|
|
28
|
-
const parts = value.toExponential().split('e');
|
|
29
|
-
const mantissaDecimals = (parts[0].split('.')[1] || '').length;
|
|
30
|
-
const exponent = parseInt(parts[1], 10);
|
|
31
|
-
if (Math.max(0, mantissaDecimals - exponent) > maxDecimalPlaces) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
};
|
|
37
|
-
return makeRule({
|
|
38
|
-
name: 'isNumber',
|
|
39
|
-
constraints: {
|
|
40
|
-
allowNaN: options?.allowNaN,
|
|
41
|
-
allowInfinity: options?.allowInfinity,
|
|
42
|
-
maxDecimalPlaces: options?.maxDecimalPlaces,
|
|
43
|
-
},
|
|
44
|
-
validate,
|
|
45
|
-
emit: (varName, ctx) => {
|
|
46
|
-
if (ctx.insideTypeGate) {
|
|
47
|
-
// typeof + isNaN already verified by gate — emit only Infinity/maxDecimalPlaces checks
|
|
48
|
-
let code = '';
|
|
49
|
-
if (!allowInfinity) {
|
|
50
|
-
code += `if (${varName} === Infinity || ${varName} === -Infinity) ${ctx.fail('isNumber')};`;
|
|
51
|
-
}
|
|
52
|
-
if (maxDecimalPlaces !== undefined) {
|
|
53
|
-
code += `${code ? '\nelse ' : ''}{ var exp=${varName}.toExponential().split('e'); var mant=(exp[0].split('.')[1]||'').length; var exp2=parseInt(exp[1],10); if(Math.max(0,mant-exp2)>${maxDecimalPlaces}) ${ctx.fail('isNumber')}; }`;
|
|
54
|
-
}
|
|
55
|
-
return code;
|
|
56
|
-
}
|
|
57
|
-
let code = `if (typeof ${varName} !== 'number') ${ctx.fail('isNumber')};`;
|
|
58
|
-
if (!allowNaN) {
|
|
59
|
-
code += `\nelse if (isNaN(${varName})) ${ctx.fail('isNumber')};`;
|
|
60
|
-
}
|
|
61
|
-
if (!allowInfinity) {
|
|
62
|
-
code += `\nelse if (${varName} === Infinity || ${varName} === -Infinity) ${ctx.fail('isNumber')};`;
|
|
63
|
-
}
|
|
64
|
-
if (maxDecimalPlaces !== undefined) {
|
|
65
|
-
code += `\nelse { var exp=${varName}.toExponential().split('e'); var mant=(exp[0].split('.')[1]||'').length; var exp2=parseInt(exp[1],10); if(Math.max(0,mant-exp2)>${maxDecimalPlaces}) ${ctx.fail('isNumber')}; }`;
|
|
66
|
-
}
|
|
67
|
-
return code;
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
-
// isBoolean — typeof check (§4.8 A)
|
|
73
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
-
export const isBoolean = makeRule({
|
|
75
|
-
name: 'isBoolean',
|
|
76
|
-
constraints: {},
|
|
77
|
-
validate: value => typeof value === 'boolean',
|
|
78
|
-
emit: (varName, ctx) => `if (typeof ${varName} !== 'boolean') ${ctx.fail('isBoolean')};`,
|
|
79
|
-
});
|
|
80
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
81
|
-
// isDate — instanceof Date + getTime() NaN check (§4.8 A)
|
|
82
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
83
|
-
export const isDate = makeRule({
|
|
84
|
-
name: 'isDate',
|
|
85
|
-
constraints: {},
|
|
86
|
-
validate: value => value instanceof Date && !isNaN(value.getTime()),
|
|
87
|
-
emit: (varName, ctx) => `if (!(${varName} instanceof Date) || isNaN(${varName}.getTime())) ${ctx.fail('isDate')};`,
|
|
88
|
-
});
|
|
89
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
-
// isEnum — factory: indexOf check using Object.values array (§4.8 C)
|
|
91
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
-
export function isEnum(entity) {
|
|
93
|
-
const values = Object.values(entity);
|
|
94
|
-
// Set lookup is O(1); array indexOf is O(n). Measured (Bun/JSC):
|
|
95
|
-
// - 4 items: indexOf 1.2 ns vs Set.has 2.2 ns (indexOf marginally faster)
|
|
96
|
-
// - 50 items: indexOf 64 ns vs Set.has 8.4 ns (Set 7.5x faster)
|
|
97
|
-
// Use Set when there are enough values to overcome its constant-factor overhead.
|
|
98
|
-
const useSet = values.length >= 8;
|
|
99
|
-
const valuesSet = useSet ? new Set(values) : null;
|
|
100
|
-
return makeRule({
|
|
101
|
-
name: 'isEnum',
|
|
102
|
-
constraints: { values },
|
|
103
|
-
validate: useSet ? value => valuesSet.has(value) : value => values.includes(value),
|
|
104
|
-
emit: (varName, ctx) => {
|
|
105
|
-
if (useSet) {
|
|
106
|
-
const i = ctx.addRef(valuesSet);
|
|
107
|
-
return `if (!refs[${i}].has(${varName})) ${ctx.fail('isEnum')};`;
|
|
108
|
-
}
|
|
109
|
-
const i = ctx.addRef(values);
|
|
110
|
-
return `if (!refs[${i}].includes(${varName})) ${ctx.fail('isEnum')};`;
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
-
// isInt — typeof + Number.isInteger check (§4.8 A)
|
|
116
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
-
export const isInt = makeRule({
|
|
118
|
-
name: 'isInt',
|
|
119
|
-
requiresType: 'number',
|
|
120
|
-
constraints: {},
|
|
121
|
-
validate: value => typeof value === 'number' && Number.isInteger(value),
|
|
122
|
-
emit: (varName, ctx) => ctx.insideTypeGate
|
|
123
|
-
? `if (!Number.isInteger(${varName})) ${ctx.fail('isInt')};`
|
|
124
|
-
: `if (typeof ${varName} !== 'number' || !Number.isInteger(${varName})) ${ctx.fail('isInt')};`,
|
|
125
|
-
});
|
|
126
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
-
// isArray — Array.isArray check (§4.8 A: operator inline)
|
|
128
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
129
|
-
export const isArray = makeRule({
|
|
130
|
-
name: 'isArray',
|
|
131
|
-
constraints: {},
|
|
132
|
-
validate: value => Array.isArray(value),
|
|
133
|
-
emit: (varName, ctx) => `if (!Array.isArray(${varName})) ${ctx.fail('isArray')};`,
|
|
134
|
-
});
|
|
135
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
136
|
-
// isObject — typeof object + non-null + non-array (§4.8 A)
|
|
137
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
|
-
export const isObject = makeRule({
|
|
139
|
-
name: 'isObject',
|
|
140
|
-
constraints: {},
|
|
141
|
-
validate: value => typeof value === 'object' && value !== null && !Array.isArray(value),
|
|
142
|
-
emit: (varName, ctx) => `if (typeof ${varName} !== 'object' || ${varName} === null || Array.isArray(${varName})) ${ctx.fail('isObject')};`,
|
|
143
|
-
});
|
|
144
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
-
// isRegExp — instanceof RegExp (self-narrowing, no typeof gate needed)
|
|
146
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
-
export const isRegExp = makeRule({
|
|
148
|
-
name: 'isRegExp',
|
|
149
|
-
constraints: {},
|
|
150
|
-
validate: value => value instanceof RegExp,
|
|
151
|
-
emit: (varName, ctx) => `if (!(${varName} instanceof RegExp)) ${ctx.fail('isRegExp')};`,
|
|
152
|
-
});
|
|
153
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
154
|
-
// isFunction — typeof check (accepts arrow fns, unlike isInstance(Function))
|
|
155
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
156
|
-
export const isFunction = makeRule({
|
|
157
|
-
name: 'isFunction',
|
|
158
|
-
constraints: {},
|
|
159
|
-
validate: value => typeof value === 'function',
|
|
160
|
-
emit: (varName, ctx) => `if (typeof ${varName} !== 'function') ${ctx.fail('isFunction')};`,
|
|
161
|
-
});
|
|
162
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
163
|
-
// isStatelessRegExp — RegExp without g/y (the only flags that mutate lastIndex
|
|
164
|
-
// across repeated test()/exec()). Safe for reuse as a single-shot matcher.
|
|
165
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
166
|
-
export const isStatelessRegExp = makeRule({
|
|
167
|
-
name: 'isStatelessRegExp',
|
|
168
|
-
constraints: {},
|
|
169
|
-
validate: value => value instanceof RegExp && !value.global && !value.sticky,
|
|
170
|
-
emit: (varName, ctx) => `if (!(${varName} instanceof RegExp) || ${varName}.global || ${varName}.sticky) ${ctx.fail('isStatelessRegExp')};`,
|
|
171
|
-
});
|
|
1
|
+
import{RequiredType as G}from"../enums.js";import{makeRule as z}from"../rule-plan.js";export const isString=z({name:"isString",constraints:{},validate:(b)=>typeof b==="string",emit:(b,g)=>`if (typeof ${b} !== 'string') ${g.fail("isString")};`});export function isNumber(b){const g=b?.allowNaN??!1,C=b?.allowInfinity??!1,A=b?.maxDecimalPlaces,B=(h)=>{if(typeof h!=="number")return!1;if(isNaN(h))return g;if(!isFinite(h))return C;if(A!==void 0){const j=h.toExponential().split("e"),E=(j[0].split(".")[1]||"").length,F=parseInt(j[1],10);if(Math.max(0,E-F)>A)return!1}return!0};return z({name:"isNumber",constraints:{allowNaN:b?.allowNaN,allowInfinity:b?.allowInfinity,maxDecimalPlaces:b?.maxDecimalPlaces},validate:B,emit:(h,j)=>{if(j.insideTypeGate){let F="";if(!C)F+=`if (${h} === Infinity || ${h} === -Infinity) ${j.fail("isNumber")};`;if(A!==void 0)F+=`${F?`
|
|
2
|
+
else `:""}{ var exp=${h}.toExponential().split('e'); var mant=(exp[0].split('.')[1]||'').length; var exp2=parseInt(exp[1],10); if(Math.max(0,mant-exp2)>${A}) ${j.fail("isNumber")}; }`;return F}let E=`if (typeof ${h} !== 'number') ${j.fail("isNumber")};`;if(!g)E+=`
|
|
3
|
+
else if (isNaN(${h})) ${j.fail("isNumber")};`;if(!C)E+=`
|
|
4
|
+
else if (${h} === Infinity || ${h} === -Infinity) ${j.fail("isNumber")};`;if(A!==void 0)E+=`
|
|
5
|
+
else { var exp=${h}.toExponential().split('e'); var mant=(exp[0].split('.')[1]||'').length; var exp2=parseInt(exp[1],10); if(Math.max(0,mant-exp2)>${A}) ${j.fail("isNumber")}; }`;return E}})}export const isBoolean=z({name:"isBoolean",constraints:{},validate:(b)=>typeof b==="boolean",emit:(b,g)=>`if (typeof ${b} !== 'boolean') ${g.fail("isBoolean")};`});export const isDate=z({name:"isDate",constraints:{},validate:(b)=>b instanceof Date&&!isNaN(b.getTime()),emit:(b,g)=>`if (!(${b} instanceof Date) || isNaN(${b}.getTime())) ${g.fail("isDate")};`});export function isEnum(b){const g=Object.values(b),C=g.length>=8,A=C?new Set(g):null;return z({name:"isEnum",constraints:{values:g},validate:C?(B)=>A.has(B):(B)=>g.includes(B),emit:(B,h)=>{if(C)return`if (!refs[${h.addRef(A)}].has(${B})) ${h.fail("isEnum")};`;return`if (!refs[${h.addRef(g)}].includes(${B})) ${h.fail("isEnum")};`}})}export const isInt=z({name:"isInt",requiresType:G.Number,constraints:{},validate:(b)=>typeof b==="number"&&Number.isInteger(b),emit:(b,g)=>g.insideTypeGate?`if (!Number.isInteger(${b})) ${g.fail("isInt")};`:`if (typeof ${b} !== 'number' || !Number.isInteger(${b})) ${g.fail("isInt")};`});export const isArray=z({name:"isArray",constraints:{},validate:(b)=>Array.isArray(b),emit:(b,g)=>`if (!Array.isArray(${b})) ${g.fail("isArray")};`});export const isObject=z({name:"isObject",constraints:{},validate:(b)=>typeof b==="object"&&b!==null&&!Array.isArray(b),emit:(b,g)=>`if (typeof ${b} !== 'object' || ${b} === null || Array.isArray(${b})) ${g.fail("isObject")};`});export const isRegExp=z({name:"isRegExp",constraints:{},validate:(b)=>b instanceof RegExp,emit:(b,g)=>`if (!(${b} instanceof RegExp)) ${g.fail("isRegExp")};`});export const isFunction=z({name:"isFunction",constraints:{},validate:(b)=>typeof b==="function",emit:(b,g)=>`if (typeof ${b} !== 'function') ${g.fail("isFunction")};`});export const isStatelessRegExp=z({name:"isStatelessRegExp",constraints:{},validate:(b)=>b instanceof RegExp&&!b.global&&!b.sticky,emit:(b,g)=>`if (!(${b} instanceof RegExp) || ${b}.global || ${b}.sticky) ${g.fail("isStatelessRegExp")};`});
|
|
@@ -1,63 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getRaw } from '../meta-access.js';
|
|
3
|
-
/**
|
|
4
|
-
* Static analysis for circular references (§4.6)
|
|
5
|
-
*
|
|
6
|
-
* Traverses the @Type reference graph via DFS to detect cycles.
|
|
7
|
-
*
|
|
8
|
-
* Flat DTO without cycles → false (zero WeakSet overhead)
|
|
9
|
-
* DTO with cycles → true (WeakSet automatically inserted)
|
|
10
|
-
*/
|
|
11
|
-
export function analyzeCircular(Class) {
|
|
12
|
-
// @Type reference graph DFS — detect back-edges via visited set
|
|
13
|
-
const visited = new Set();
|
|
14
|
-
function walk(cls) {
|
|
15
|
-
if (visited.has(cls)) {
|
|
16
|
-
return true;
|
|
17
|
-
} // back-edge → cycle detected
|
|
18
|
-
visited.add(cls);
|
|
19
|
-
const raw = getRaw(cls);
|
|
20
|
-
if (raw) {
|
|
21
|
-
for (const meta of Object.values(raw)) {
|
|
22
|
-
// Simple @Type
|
|
23
|
-
if (meta.type?.fn) {
|
|
24
|
-
let typeResult;
|
|
25
|
-
try {
|
|
26
|
-
typeResult = meta.type.fn();
|
|
27
|
-
}
|
|
28
|
-
catch (e) {
|
|
29
|
-
throw new BakerError(`${cls.name}: type function threw: ${e.message}`, { cause: e });
|
|
30
|
-
}
|
|
31
|
-
const nested = Array.isArray(typeResult) ? typeResult[0] : typeResult;
|
|
32
|
-
if (walk(nested)) {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// discriminator subTypes
|
|
37
|
-
if (meta.type?.discriminator) {
|
|
38
|
-
for (const sub of meta.type.discriminator.subTypes) {
|
|
39
|
-
if (walk(sub.value)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
// W1 (F-1): collectionValue thunk (Set/Map nested DTO) — invoke and walk
|
|
45
|
-
if (meta.type?.collectionValue) {
|
|
46
|
-
let resolved;
|
|
47
|
-
try {
|
|
48
|
-
resolved = meta.type.collectionValue();
|
|
49
|
-
}
|
|
50
|
-
catch (e) {
|
|
51
|
-
throw new BakerError(`${cls.name}: collectionValue function threw: ${e.message}`, { cause: e });
|
|
52
|
-
}
|
|
53
|
-
if (typeof resolved === 'function' && walk(resolved)) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
visited.delete(cls); // Release tree edge — prevent false positives for diamond patterns
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
return walk(Class);
|
|
63
|
-
}
|
|
1
|
+
import{BakerError as G}from"../errors.js";import{getRaw as I}from"../meta-access.js";export function analyzeCircular(H){const A=new Set;function x(j){if(A.has(j))return!0;A.add(j);const D=I(j);if(D)for(const q of Object.values(D)){if(q.type?.fn){let h;try{h=q.type.fn()}catch(F){throw new G(`${j.name}: type function threw: ${F.message}`,{cause:F})}const z=Array.isArray(h)?h[0]:h;if(x(z))return!0}if(q.type?.discriminator){for(const h of q.type.discriminator.subTypes)if(x(h.value))return!0}if(q.type?.collectionValue){let h;try{h=q.type.collectionValue()}catch(z){throw new G(`${j.name}: collectionValue function threw: ${z.message}`,{cause:z})}if(typeof h==="function"&&x(h))return!0}}A.delete(j);return!1}return x(H)}
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// Shared code-generation utilities for deserialize/serialize builders
|
|
3
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
-
/** Convert key to a valid JS identifier suffix (encode non-alphanumeric chars via charCode to prevent collisions) */
|
|
5
|
-
export function sanitizeKey(key) {
|
|
6
|
-
return key.replace(/[^a-zA-Z0-9_]/g, ch => `$${ch.charCodeAt(0)}$`);
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Generate a groups-has expression for the fast-path single-group / Set pattern.
|
|
10
|
-
* Checks if any of the given groups match the runtime groups.
|
|
11
|
-
*/
|
|
12
|
-
export function buildGroupsHasExpr(singleGroupVar, groupsVar, groups) {
|
|
13
|
-
const checks = groups.map(group => {
|
|
14
|
-
const q = JSON.stringify(group);
|
|
15
|
-
return `(${singleGroupVar}===${q} || (${groupsVar} && ${groupsVar}.has(${q})))`;
|
|
16
|
-
});
|
|
17
|
-
return checks.join(' || ');
|
|
18
|
-
}
|
|
1
|
+
export function sanitizeKey(d){return d.replace(/[^a-zA-Z0-9_]/g,(b)=>`$${b.charCodeAt(0)}$`)}export function buildGroupsHasExpr(d,b,w){return w.map((z)=>{const j=JSON.stringify(z);return`(${d}===${j} || (${b} && ${b}.has(${j})))`}).join(" || ")}
|