@uniqu/url 0.1.0 → 0.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.
package/dist/builder.cjs CHANGED
@@ -91,6 +91,7 @@ function serializeValue(value) {
91
91
  const KNOWN_CONTROL_KEYS = new Set([
92
92
  "$select",
93
93
  "$groupBy",
94
+ "$having",
94
95
  "$sort",
95
96
  "$limit",
96
97
  "$skip",
@@ -122,6 +123,13 @@ function serializeControls(controls) {
122
123
  const part = `$groupBy=${seg}`;
123
124
  result = result ? result + "&" + part : part;
124
125
  }
126
+ if (controls.$having) {
127
+ const havingStr = serializeFilter(controls.$having);
128
+ if (havingStr) {
129
+ const part = "$and" in controls.$having ? `$having=(${havingStr})` : `$having=${havingStr}`;
130
+ result = result ? result + "&" + part : part;
131
+ }
132
+ }
125
133
  if (controls.$sort) {
126
134
  let seg = "";
127
135
  for (const [field, dir] of Object.entries(controls.$sort)) {
package/dist/builder.mjs CHANGED
@@ -90,6 +90,7 @@ function serializeValue(value) {
90
90
  const KNOWN_CONTROL_KEYS = new Set([
91
91
  "$select",
92
92
  "$groupBy",
93
+ "$having",
93
94
  "$sort",
94
95
  "$limit",
95
96
  "$skip",
@@ -121,6 +122,13 @@ function serializeControls(controls) {
121
122
  const part = `$groupBy=${seg}`;
122
123
  result = result ? result + "&" + part : part;
123
124
  }
125
+ if (controls.$having) {
126
+ const havingStr = serializeFilter(controls.$having);
127
+ if (havingStr) {
128
+ const part = "$and" in controls.$having ? `$having=(${havingStr})` : `$having=${havingStr}`;
129
+ result = result ? result + "&" + part : part;
130
+ }
131
+ }
124
132
  if (controls.$sort) {
125
133
  let seg = "";
126
134
  for (const [field, dir] of Object.entries(controls.$sort)) {
package/dist/index.cjs CHANGED
@@ -310,9 +310,10 @@ function unescapeString(str) {
310
310
  merged.push(currentMerge);
311
311
  currentMerge = {};
312
312
  } else {
313
- currentMerge[key] = {};
314
- for (const op of currentOps) currentMerge[key][op] = (0, _uniqu_core.isPrimitive)(currentVal) ? currentVal : currentVal[op];
315
- for (const op of otherOps) currentMerge[key][op] = (0, _uniqu_core.isPrimitive)(val) ? val : val[op];
313
+ const m = {};
314
+ for (const op of currentOps) m[op] = (0, _uniqu_core.isPrimitive)(currentVal) ? currentVal : currentVal[op];
315
+ for (const op of otherOps) m[op] = (0, _uniqu_core.isPrimitive)(val) ? val : val[op];
316
+ currentMerge[key] = m;
316
317
  }
317
318
  } else currentMerge[key] = val;
318
319
  }
@@ -350,9 +351,9 @@ function buildExists(fields, positive) {
350
351
  let filter = {};
351
352
  let parser;
352
353
  if (exprParts.length) {
353
- parser = new Parser(lex(exprParts.join("&")));
354
- filter = parser.parseExpression();
355
- parser.expectEof();
354
+ const parsed = parseFilterExpr(exprParts.join("&"));
355
+ parser = parsed.parser;
356
+ filter = parsed.expr;
356
357
  } else parser = new Parser([]);
357
358
  for (const [field, op] of controlInsights) parser.captureInsight(field, op);
358
359
  return {
@@ -374,6 +375,15 @@ function buildExists(fields, positive) {
374
375
  parts.push(str.slice(start));
375
376
  return parts;
376
377
  }
378
+ /** Lex + parse a raw filter expression string. */ function parseFilterExpr(raw) {
379
+ const parser = new Parser(lex(raw));
380
+ const expr = parser.parseExpression();
381
+ parser.expectEof();
382
+ return {
383
+ expr,
384
+ parser
385
+ };
386
+ }
377
387
  /** Parse a single `$with` segment like `posts` or `posts($sort=-createdAt&status=active)`. */ function parseWithSegment(seg) {
378
388
  if (!seg) return null;
379
389
  const parenIdx = seg.indexOf("(");
@@ -500,6 +510,14 @@ function handleControls(parts) {
500
510
  controlInsights.push([f, "$groupBy"]);
501
511
  }
502
512
  break;
513
+ case "$having": {
514
+ if (!value) break;
515
+ const { expr, parser: hp } = parseFilterExpr(value);
516
+ if (controls.$having) controls.$having = { $and: [controls.$having, expr] };
517
+ else controls.$having = expr;
518
+ for (const [field] of hp.getInsights()) controlInsights.push([field, "$having"]);
519
+ break;
520
+ }
503
521
  case "$limit":
504
522
  case "$top":
505
523
  controls.$limit = Number(value);
package/dist/index.mjs CHANGED
@@ -309,9 +309,10 @@ function unescapeString(str) {
309
309
  merged.push(currentMerge);
310
310
  currentMerge = {};
311
311
  } else {
312
- currentMerge[key] = {};
313
- for (const op of currentOps) currentMerge[key][op] = isPrimitive(currentVal) ? currentVal : currentVal[op];
314
- for (const op of otherOps) currentMerge[key][op] = isPrimitive(val) ? val : val[op];
312
+ const m = {};
313
+ for (const op of currentOps) m[op] = isPrimitive(currentVal) ? currentVal : currentVal[op];
314
+ for (const op of otherOps) m[op] = isPrimitive(val) ? val : val[op];
315
+ currentMerge[key] = m;
315
316
  }
316
317
  } else currentMerge[key] = val;
317
318
  }
@@ -349,9 +350,9 @@ function buildExists(fields, positive) {
349
350
  let filter = {};
350
351
  let parser;
351
352
  if (exprParts.length) {
352
- parser = new Parser(lex(exprParts.join("&")));
353
- filter = parser.parseExpression();
354
- parser.expectEof();
353
+ const parsed = parseFilterExpr(exprParts.join("&"));
354
+ parser = parsed.parser;
355
+ filter = parsed.expr;
355
356
  } else parser = new Parser([]);
356
357
  for (const [field, op] of controlInsights) parser.captureInsight(field, op);
357
358
  return {
@@ -373,6 +374,15 @@ function buildExists(fields, positive) {
373
374
  parts.push(str.slice(start));
374
375
  return parts;
375
376
  }
377
+ /** Lex + parse a raw filter expression string. */ function parseFilterExpr(raw) {
378
+ const parser = new Parser(lex(raw));
379
+ const expr = parser.parseExpression();
380
+ parser.expectEof();
381
+ return {
382
+ expr,
383
+ parser
384
+ };
385
+ }
376
386
  /** Parse a single `$with` segment like `posts` or `posts($sort=-createdAt&status=active)`. */ function parseWithSegment(seg) {
377
387
  if (!seg) return null;
378
388
  const parenIdx = seg.indexOf("(");
@@ -499,6 +509,14 @@ function handleControls(parts) {
499
509
  controlInsights.push([f, "$groupBy"]);
500
510
  }
501
511
  break;
512
+ case "$having": {
513
+ if (!value) break;
514
+ const { expr, parser: hp } = parseFilterExpr(value);
515
+ if (controls.$having) controls.$having = { $and: [controls.$having, expr] };
516
+ else controls.$having = expr;
517
+ for (const [field] of hp.getInsights()) controlInsights.push([field, "$having"]);
518
+ break;
519
+ }
502
520
  case "$limit":
503
521
  case "$top":
504
522
  controls.$limit = Number(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniqu/url",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "URL query string parser producing the Uniqu canonical query format",
5
5
  "license": "MIT",
6
6
  "author": "Artem Maltsev",
@@ -45,7 +45,7 @@
45
45
  "dist"
46
46
  ],
47
47
  "dependencies": {
48
- "@uniqu/core": "^0.1.0"
48
+ "@uniqu/core": "^0.1.1"
49
49
  },
50
50
  "scripts": {
51
51
  "pub": "pnpm publish --access public",