@adonis-agora/filter 0.0.0-beta-20261110131309

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 (168) hide show
  1. package/CHANGELOG.md +417 -0
  2. package/LICENSE +21 -0
  3. package/README.md +75 -0
  4. package/dist/commands/main.d.ts +19 -0
  5. package/dist/commands/main.d.ts.map +1 -0
  6. package/dist/commands/main.js +19 -0
  7. package/dist/commands/main.js.map +1 -0
  8. package/dist/commands/make_filter.d.ts +18 -0
  9. package/dist/commands/make_filter.d.ts.map +1 -0
  10. package/dist/commands/make_filter.js +32 -0
  11. package/dist/commands/make_filter.js.map +1 -0
  12. package/dist/commands/make_filter_client.d.ts +35 -0
  13. package/dist/commands/make_filter_client.d.ts.map +1 -0
  14. package/dist/commands/make_filter_client.js +85 -0
  15. package/dist/commands/make_filter_client.js.map +1 -0
  16. package/dist/configure.d.ts +15 -0
  17. package/dist/configure.d.ts.map +1 -0
  18. package/dist/configure.js +20 -0
  19. package/dist/configure.js.map +1 -0
  20. package/dist/providers/filter_provider.d.ts +28 -0
  21. package/dist/providers/filter_provider.d.ts.map +1 -0
  22. package/dist/providers/filter_provider.js +33 -0
  23. package/dist/providers/filter_provider.js.map +1 -0
  24. package/dist/src/aggregate.d.ts +70 -0
  25. package/dist/src/aggregate.d.ts.map +1 -0
  26. package/dist/src/aggregate.js +195 -0
  27. package/dist/src/aggregate.js.map +1 -0
  28. package/dist/src/aggregate_path.d.ts +25 -0
  29. package/dist/src/aggregate_path.d.ts.map +1 -0
  30. package/dist/src/aggregate_path.js +35 -0
  31. package/dist/src/aggregate_path.js.map +1 -0
  32. package/dist/src/apply_from_request.d.ts +85 -0
  33. package/dist/src/apply_from_request.d.ts.map +1 -0
  34. package/dist/src/apply_from_request.js +164 -0
  35. package/dist/src/apply_from_request.js.map +1 -0
  36. package/dist/src/base_filter.d.ts +49 -0
  37. package/dist/src/base_filter.d.ts.map +1 -0
  38. package/dist/src/base_filter.js +33 -0
  39. package/dist/src/base_filter.js.map +1 -0
  40. package/dist/src/base_model_filter.d.ts +90 -0
  41. package/dist/src/base_model_filter.d.ts.map +1 -0
  42. package/dist/src/base_model_filter.js +85 -0
  43. package/dist/src/base_model_filter.js.map +1 -0
  44. package/dist/src/coerce_value.d.ts +26 -0
  45. package/dist/src/coerce_value.d.ts.map +1 -0
  46. package/dist/src/coerce_value.js +82 -0
  47. package/dist/src/coerce_value.js.map +1 -0
  48. package/dist/src/cursor.d.ts +92 -0
  49. package/dist/src/cursor.d.ts.map +1 -0
  50. package/dist/src/cursor.js +107 -0
  51. package/dist/src/cursor.js.map +1 -0
  52. package/dist/src/custom_filter.d.ts +36 -0
  53. package/dist/src/custom_filter.d.ts.map +1 -0
  54. package/dist/src/custom_filter.js +127 -0
  55. package/dist/src/custom_filter.js.map +1 -0
  56. package/dist/src/decorator_metadata.d.ts +41 -0
  57. package/dist/src/decorator_metadata.d.ts.map +1 -0
  58. package/dist/src/decorator_metadata.js +83 -0
  59. package/dist/src/decorator_metadata.js.map +1 -0
  60. package/dist/src/decorators.d.ts +73 -0
  61. package/dist/src/decorators.d.ts.map +1 -0
  62. package/dist/src/decorators.js +118 -0
  63. package/dist/src/decorators.js.map +1 -0
  64. package/dist/src/escape-like.d.ts +10 -0
  65. package/dist/src/escape-like.d.ts.map +1 -0
  66. package/dist/src/escape-like.js +15 -0
  67. package/dist/src/escape-like.js.map +1 -0
  68. package/dist/src/field_aliases.d.ts +30 -0
  69. package/dist/src/field_aliases.d.ts.map +1 -0
  70. package/dist/src/field_aliases.js +65 -0
  71. package/dist/src/field_aliases.js.map +1 -0
  72. package/dist/src/filter_class.d.ts +46 -0
  73. package/dist/src/filter_class.d.ts.map +1 -0
  74. package/dist/src/filter_class.js +244 -0
  75. package/dist/src/filter_class.js.map +1 -0
  76. package/dist/src/filter_spec.d.ts +231 -0
  77. package/dist/src/filter_spec.d.ts.map +1 -0
  78. package/dist/src/filter_spec.js +182 -0
  79. package/dist/src/filter_spec.js.map +1 -0
  80. package/dist/src/filterable_mixin.d.ts +49 -0
  81. package/dist/src/filterable_mixin.d.ts.map +1 -0
  82. package/dist/src/filterable_mixin.js +47 -0
  83. package/dist/src/filterable_mixin.js.map +1 -0
  84. package/dist/src/generate_client.d.ts +107 -0
  85. package/dist/src/generate_client.d.ts.map +1 -0
  86. package/dist/src/generate_client.js +164 -0
  87. package/dist/src/generate_client.js.map +1 -0
  88. package/dist/src/group_by_count.d.ts +67 -0
  89. package/dist/src/group_by_count.d.ts.map +1 -0
  90. package/dist/src/group_by_count.js +71 -0
  91. package/dist/src/group_by_count.js.map +1 -0
  92. package/dist/src/in_memory.d.ts +279 -0
  93. package/dist/src/in_memory.d.ts.map +1 -0
  94. package/dist/src/in_memory.js +810 -0
  95. package/dist/src/in_memory.js.map +1 -0
  96. package/dist/src/index.d.ts +51 -0
  97. package/dist/src/index.d.ts.map +1 -0
  98. package/dist/src/index.js +33 -0
  99. package/dist/src/index.js.map +1 -0
  100. package/dist/src/lucid_adapter.d.ts +311 -0
  101. package/dist/src/lucid_adapter.d.ts.map +1 -0
  102. package/dist/src/lucid_adapter.js +469 -0
  103. package/dist/src/lucid_adapter.js.map +1 -0
  104. package/dist/src/lucid_macros.d.ts +87 -0
  105. package/dist/src/lucid_macros.d.ts.map +1 -0
  106. package/dist/src/lucid_macros.js +67 -0
  107. package/dist/src/lucid_macros.js.map +1 -0
  108. package/dist/src/normalizer.d.ts +21 -0
  109. package/dist/src/normalizer.d.ts.map +1 -0
  110. package/dist/src/normalizer.js +61 -0
  111. package/dist/src/normalizer.js.map +1 -0
  112. package/dist/src/operators.d.ts +37 -0
  113. package/dist/src/operators.d.ts.map +1 -0
  114. package/dist/src/operators.js +43 -0
  115. package/dist/src/operators.js.map +1 -0
  116. package/dist/src/paginate.d.ts +12 -0
  117. package/dist/src/paginate.d.ts.map +1 -0
  118. package/dist/src/paginate.js +88 -0
  119. package/dist/src/paginate.js.map +1 -0
  120. package/dist/src/pagination_stash.d.ts +4 -0
  121. package/dist/src/pagination_stash.d.ts.map +1 -0
  122. package/dist/src/pagination_stash.js +19 -0
  123. package/dist/src/pagination_stash.js.map +1 -0
  124. package/dist/src/parse_request.d.ts +50 -0
  125. package/dist/src/parse_request.d.ts.map +1 -0
  126. package/dist/src/parse_request.js +220 -0
  127. package/dist/src/parse_request.js.map +1 -0
  128. package/dist/src/runner.d.ts +65 -0
  129. package/dist/src/runner.d.ts.map +1 -0
  130. package/dist/src/runner.js +371 -0
  131. package/dist/src/runner.js.map +1 -0
  132. package/dist/src/source_resolver.d.ts +18 -0
  133. package/dist/src/source_resolver.d.ts.map +1 -0
  134. package/dist/src/source_resolver.js +64 -0
  135. package/dist/src/source_resolver.js.map +1 -0
  136. package/dist/src/spatie_parser.d.ts +39 -0
  137. package/dist/src/spatie_parser.d.ts.map +1 -0
  138. package/dist/src/spatie_parser.js +134 -0
  139. package/dist/src/spatie_parser.js.map +1 -0
  140. package/dist/src/testing.d.ts +73 -0
  141. package/dist/src/testing.d.ts.map +1 -0
  142. package/dist/src/testing.js +136 -0
  143. package/dist/src/testing.js.map +1 -0
  144. package/dist/src/types.d.ts +226 -0
  145. package/dist/src/types.d.ts.map +1 -0
  146. package/dist/src/types.js +2 -0
  147. package/dist/src/types.js.map +1 -0
  148. package/dist/src/validate-column-filter.d.ts +30 -0
  149. package/dist/src/validate-column-filter.d.ts.map +1 -0
  150. package/dist/src/validate-column-filter.js +148 -0
  151. package/dist/src/validate-column-filter.js.map +1 -0
  152. package/dist/src/value-shape.d.ts +18 -0
  153. package/dist/src/value-shape.d.ts.map +1 -0
  154. package/dist/src/value-shape.js +36 -0
  155. package/dist/src/value-shape.js.map +1 -0
  156. package/dist/stubs/main.d.ts +3 -0
  157. package/dist/stubs/main.d.ts.map +1 -0
  158. package/dist/stubs/main.js +4 -0
  159. package/dist/stubs/main.js.map +1 -0
  160. package/dist/stubs/make/filter/main.stub +45 -0
  161. package/package.json +102 -0
  162. package/skills/filter-basics/SKILL.md +285 -0
  163. package/skills/filter-codegen/SKILL.md +246 -0
  164. package/skills/filter-definitions/SKILL.md +340 -0
  165. package/skills/filter-querying/SKILL.md +319 -0
  166. package/skills/filter-safety/SKILL.md +304 -0
  167. package/stubs/main.ts +4 -0
  168. package/stubs/make/filter/main.stub +45 -0
@@ -0,0 +1,21 @@
1
+ import type { InputNormalizer } from './types.js';
2
+ export interface NormalizeOptions {
3
+ /** Key transform — a built-in case mode or a custom mapping function. */
4
+ normalizer: InputNormalizer;
5
+ /** Strip a trailing `Id`/`_id` (and drop a bare `id`) from each key. Default false. */
6
+ dropId?: boolean;
7
+ /** Strip `null`/`undefined`/empty-string values. Default true. */
8
+ stripEmpty?: boolean;
9
+ }
10
+ /**
11
+ * Normalize a decoded input object's top-level keys — case-transforming them via
12
+ * the chosen {@link InputNormalizer}, optionally stripping `Id` suffixes and
13
+ * empty values — into a null-prototype object.
14
+ *
15
+ * Prototype-pollution keys (`__proto__`, `constructor`, `prototype`, `toString`,
16
+ * `valueOf`) are dropped both before and after normalization, and the result has
17
+ * a null prototype, so a hostile key can never reach `Object.prototype`. Nested
18
+ * values are left untouched — only top-level keys are normalized.
19
+ */
20
+ export declare function normalizeInput(input: unknown, options: NormalizeOptions): Record<string, unknown>;
21
+ //# sourceMappingURL=normalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,UAAU,EAAE,eAAe,CAAC;IAC5B,uFAAuF;IACvF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBjG"}
@@ -0,0 +1,61 @@
1
+ const BLOCKED_KEYS = new Set(['__proto__', 'constructor', 'prototype', 'toString', 'valueOf']);
2
+ /**
3
+ * Normalize a decoded input object's top-level keys — case-transforming them via
4
+ * the chosen {@link InputNormalizer}, optionally stripping `Id` suffixes and
5
+ * empty values — into a null-prototype object.
6
+ *
7
+ * Prototype-pollution keys (`__proto__`, `constructor`, `prototype`, `toString`,
8
+ * `valueOf`) are dropped both before and after normalization, and the result has
9
+ * a null prototype, so a hostile key can never reach `Object.prototype`. Nested
10
+ * values are left untouched — only top-level keys are normalized.
11
+ */
12
+ export function normalizeInput(input, options) {
13
+ if (input == null || typeof input !== 'object')
14
+ return {};
15
+ const norm = pickNormalizer(options.normalizer);
16
+ const drop = options.dropId === true;
17
+ const strip = options.stripEmpty !== false;
18
+ const out = Object.create(null);
19
+ for (const [rawKey, value] of Object.entries(input)) {
20
+ if (BLOCKED_KEYS.has(rawKey))
21
+ continue;
22
+ if (strip && (value === null || value === undefined || value === ''))
23
+ continue;
24
+ let key = norm(rawKey);
25
+ if (BLOCKED_KEYS.has(key))
26
+ continue;
27
+ if (drop)
28
+ key = stripId(key);
29
+ if (key.length === 0)
30
+ continue;
31
+ out[key] = value;
32
+ }
33
+ return out;
34
+ }
35
+ function pickNormalizer(spec) {
36
+ if (typeof spec === 'function')
37
+ return spec;
38
+ if (spec === 'camelCase')
39
+ return toCamelCase;
40
+ return toSnakeCase;
41
+ }
42
+ function toCamelCase(key) {
43
+ const result = key.replace(/[_-](\w)/g, (_, c) => c.toUpperCase());
44
+ return result.charAt(0).toLowerCase() + result.slice(1);
45
+ }
46
+ function toSnakeCase(key) {
47
+ return key
48
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
49
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
50
+ .toLowerCase();
51
+ }
52
+ function stripId(key) {
53
+ if (key.endsWith('Id'))
54
+ return key.slice(0, -2);
55
+ if (key.endsWith('_id'))
56
+ return key.slice(0, -3);
57
+ if (key === 'id')
58
+ return '';
59
+ return key;
60
+ }
61
+ //# sourceMappingURL=normalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizer.js","sourceRoot":"","sources":["../../src/normalizer.ts"],"names":[],"mappings":"AAWA,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;AAE/F;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,OAAyB;IACtE,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC1D,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC;IAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA4B,CAAC;IAC3D,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QAC/E,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,SAAS;QACvC,IAAI,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC;YAAE,SAAS;QAC/E,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACpC,IAAI,IAAI;YAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,IAAqB;IAC3C,IAAI,OAAO,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,WAAW,CAAC;IAC7C,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAE,CAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG;SACP,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC;SACzC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChD,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Supported filter operators for generic column-based filtering.
3
+ *
4
+ * Compatible with flip-nestjs SqlQueryBuilderService ColumnFilter[].
5
+ */
6
+ export type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'iContains' | 'startsWith' | 'endsWith' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'notBetween' | 'in' | 'notIn' | 'isAnyOf' | 'isEmpty' | 'isNotEmpty' | 'isNull' | 'isNotNull' | 'exists' | 'notExists';
7
+ /**
8
+ * All valid operator strings, used for runtime validation.
9
+ */
10
+ export declare const FILTER_OPERATORS: readonly FilterOperator[];
11
+ /**
12
+ * SQL-symbol aliases accepted on input and normalized to a canonical
13
+ * {@link FilterOperator} before validation and query building. Lets callers
14
+ * write the familiar `=`/`!=`/`<`/`>` shorthands instead of the named form.
15
+ */
16
+ export declare const OPERATOR_ALIASES: Readonly<Record<string, FilterOperator>>;
17
+ /**
18
+ * An operator alias string (e.g. `'='`). Always normalized to a canonical
19
+ * {@link FilterOperator} via `normalizeOperator`.
20
+ */
21
+ export type FilterOperatorAlias = keyof typeof OPERATOR_ALIASES;
22
+ /**
23
+ * Operator value accepted on input: either a canonical {@link FilterOperator}
24
+ * or one of its SQL-symbol {@link FilterOperatorAlias aliases}.
25
+ */
26
+ export type FilterOperatorInput = FilterOperator | FilterOperatorAlias;
27
+ /**
28
+ * A single column filter condition with optional AND/OR composition.
29
+ */
30
+ export interface ColumnFilter {
31
+ field: string;
32
+ operator: FilterOperatorInput;
33
+ value?: unknown;
34
+ AND?: ColumnFilter[];
35
+ OR?: ColumnFilter[];
36
+ }
37
+ //# sourceMappingURL=operators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/operators.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,WAAW,GACX,UAAU,GACV,aAAa,GACb,WAAW,GACX,YAAY,GACZ,UAAU,GACV,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,SAAS,GACT,YAAY,GACZ,IAAI,GACJ,OAAO,GACP,SAAS,GACT,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,WAAW,CAAC;AAEhB;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAuB5C,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAS5D,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,gBAAgB,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;CACrB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * All valid operator strings, used for runtime validation.
3
+ */
4
+ export const FILTER_OPERATORS = [
5
+ 'equals',
6
+ 'notEquals',
7
+ 'contains',
8
+ 'notContains',
9
+ 'iContains',
10
+ 'startsWith',
11
+ 'endsWith',
12
+ 'gt',
13
+ 'gte',
14
+ 'lt',
15
+ 'lte',
16
+ 'between',
17
+ 'notBetween',
18
+ 'in',
19
+ 'notIn',
20
+ 'isAnyOf',
21
+ 'isEmpty',
22
+ 'isNotEmpty',
23
+ 'isNull',
24
+ 'isNotNull',
25
+ 'exists',
26
+ 'notExists',
27
+ ];
28
+ /**
29
+ * SQL-symbol aliases accepted on input and normalized to a canonical
30
+ * {@link FilterOperator} before validation and query building. Lets callers
31
+ * write the familiar `=`/`!=`/`<`/`>` shorthands instead of the named form.
32
+ */
33
+ export const OPERATOR_ALIASES = {
34
+ '=': 'equals',
35
+ '==': 'equals',
36
+ '!=': 'notEquals',
37
+ '<>': 'notEquals',
38
+ '>': 'gt',
39
+ '>=': 'gte',
40
+ '<': 'lt',
41
+ '<=': 'lte',
42
+ };
43
+ //# sourceMappingURL=operators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operators.js","sourceRoot":"","sources":["../../src/operators.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA8B;IACzD,QAAQ;IACR,WAAW;IACX,UAAU;IACV,aAAa;IACb,WAAW;IACX,YAAY;IACZ,UAAU;IACV,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,KAAK;IACL,SAAS;IACT,YAAY;IACZ,IAAI;IACJ,OAAO;IACP,SAAS;IACT,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,QAAQ;IACR,WAAW;CACH,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA6C;IACxE,GAAG,EAAE,QAAQ;IACb,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,WAAW;IACjB,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,IAAI;IACT,IAAI,EAAE,KAAK;CACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Lucid's `query.paginate(page, perPage)`, fetching the page before the total.
3
+ *
4
+ * Lucid counts every matching row before it reads the page. A page that comes back shorter than
5
+ * `perPage` already pins the total (`offset + rows`), so the count runs only for a full page, or
6
+ * an empty page past the first. Same paginator class, same meta, same `after:*` hooks. A model
7
+ * with a `before:paginate` or `before:fetch` hook is left to Lucid, since those hooks may change
8
+ * the count query and the page query differently; so is a select list that changes the row count
9
+ * (see {@link selectKeepsRowCount}).
10
+ */
11
+ export declare function paginate(query: object, page: number, perPage: number): unknown;
12
+ //# sourceMappingURL=paginate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paginate.d.ts","sourceRoot":"","sources":["../../src/paginate.ts"],"names":[],"mappings":"AAwEA;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB9E"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Lucid counts the query with its select list cleared, so the page has as many rows as the count
3
+ * only while the select list cannot change how many rows come back: plain columns and scalar
4
+ * subqueries (`withCount`). A `distinct`, an aggregate, a raw expression or a `union` can, and
5
+ * then the page does not pin Lucid's total.
6
+ */
7
+ function selectKeepsRowCount(statements) {
8
+ if (!statements)
9
+ return false;
10
+ return statements.every((statement) => {
11
+ if (statement.grouping === 'union')
12
+ return false;
13
+ if (statement.grouping !== 'columns')
14
+ return true;
15
+ if (statement.distinct || statement.distinctOn || statement.type !== undefined)
16
+ return false;
17
+ return (Array.isArray(statement.value) &&
18
+ statement.value.every((column) => typeof column === 'string' ||
19
+ (typeof column === 'object' &&
20
+ column !== null &&
21
+ !column.isRawInstance)));
22
+ });
23
+ }
24
+ function isLucidModelQuery(query) {
25
+ const q = query;
26
+ return (typeof q.forPage === 'function' &&
27
+ typeof q.execQuery === 'function' &&
28
+ typeof q.clone === 'function' &&
29
+ typeof q.model?.$hooks?.has === 'function');
30
+ }
31
+ /**
32
+ * Lucid's `query.paginate(page, perPage)`, fetching the page before the total.
33
+ *
34
+ * Lucid counts every matching row before it reads the page. A page that comes back shorter than
35
+ * `perPage` already pins the total (`offset + rows`), so the count runs only for a full page, or
36
+ * an empty page past the first. Same paginator class, same meta, same `after:*` hooks. A model
37
+ * with a `before:paginate` or `before:fetch` hook is left to Lucid, since those hooks may change
38
+ * the count query and the page query differently; so is a select list that changes the row count
39
+ * (see {@link selectKeepsRowCount}).
40
+ */
41
+ export function paginate(query, page, perPage) {
42
+ const q = query;
43
+ page = Number(page);
44
+ perPage = Number(perPage);
45
+ if (!isLucidModelQuery(query) ||
46
+ q.model.$hooks.has('before:paginate') ||
47
+ q.model.$hooks.has('before:fetch') ||
48
+ !selectKeepsRowCount(q.knexQuery._statements) ||
49
+ !Number.isInteger(page) ||
50
+ page < 1 ||
51
+ !Number.isInteger(perPage) ||
52
+ perPage < 1) {
53
+ return q.paginate(page, perPage);
54
+ }
55
+ return pageThenCount(q, page, perPage);
56
+ }
57
+ async function pageThenCount(q, page, perPage) {
58
+ const isFetchCall = q.wrapResultsToModelInstances && q.knexQuery._method === 'select';
59
+ const countQuery = q
60
+ .clone()
61
+ .clearOrder()
62
+ .clearLimit()
63
+ .clearOffset()
64
+ .clearSelect()
65
+ .count('* as total')
66
+ .pojo();
67
+ const results = await q.forPage(page, perPage).execQuery();
68
+ let total;
69
+ if (results.length < perPage && (results.length > 0 || page === 1)) {
70
+ total = (page - 1) * perPage + results.length;
71
+ }
72
+ else {
73
+ const aggregate = await countQuery.exec();
74
+ total = q.hasGroupBy ? aggregate.length : aggregate[0]?.total;
75
+ }
76
+ const { ModelPaginator } = await import('@adonisjs/lucid/orm');
77
+ const { SimplePaginator } = await import('@adonisjs/lucid/database');
78
+ const paginator = q.wrapResultsToModelInstances
79
+ ? new ModelPaginator(total, perPage, page, ...results)
80
+ : new SimplePaginator(total, perPage, page, ...results);
81
+ paginator.namingStrategy = q.model.namingStrategy;
82
+ if (isFetchCall) {
83
+ await q.model.$hooks.runner('after:paginate').run(paginator);
84
+ await q.model.$hooks.runner('after:fetch').run(results);
85
+ }
86
+ return paginator;
87
+ }
88
+ //# sourceMappingURL=paginate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paginate.js","sourceRoot":"","sources":["../../src/paginate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ResolvedPagination } from './runner.js';
2
+ export declare function stashPagination(query: unknown, pagination: ResolvedPagination): void;
3
+ export declare function readPagination(query: unknown): ResolvedPagination | undefined;
4
+ //# sourceMappingURL=pagination_stash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination_stash.d.ts","sourceRoot":"","sources":["../../src/pagination_stash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAWtD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAIpF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,GAAG,SAAS,CAG7E"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Where a filtered query remembers the pagination the request resolved to.
3
+ *
4
+ * The builder is handed back to the caller so the endpoint can keep composing on it; the page and
5
+ * size it already resolved ride along on this symbol, so a later `filterPaginate()` needs no
6
+ * arguments and no second read of the request.
7
+ */
8
+ const PAGINATION = Symbol.for('@adonis-agora/filter:pagination');
9
+ export function stashPagination(query, pagination) {
10
+ if (query !== null && typeof query === 'object') {
11
+ query[PAGINATION] = pagination;
12
+ }
13
+ }
14
+ export function readPagination(query) {
15
+ if (query === null || typeof query !== 'object')
16
+ return undefined;
17
+ return query[PAGINATION];
18
+ }
19
+ //# sourceMappingURL=pagination_stash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination_stash.js","sourceRoot":"","sources":["../../src/pagination_stash.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AAEjE,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,UAA8B;IAC5E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/C,KAAiC,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC;IAC9D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAClE,OAAQ,KAAwD,CAAC,UAAU,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { ColumnFilter } from './operators.js';
2
+ import type { FilterInput, GroupByCountRequest, SortItem } from './types.js';
3
+ /** Map one `filter[field]=…` entry to `ColumnFilter[]` (Spatie/JSON:API shapes). */
4
+ export declare function toColumnFilters(field: string, value: unknown): ColumnFilter[];
5
+ /**
6
+ * Parse the `distinct` param into a de-duplicated list of field names. Accepts a
7
+ * comma-separated string (`distinct=afsc,base`) or a repeated/array form
8
+ * (`distinct[]=afsc&distinct[]=base`) — the shapes the client's `toQueryString()`
9
+ * and structured `build()` emit. Non-string entries are ignored.
10
+ */
11
+ export declare function parseDistinct(distinct: unknown): string[];
12
+ /**
13
+ * Parse the `sort` param into ordered {@link SortItem}s. Accepts the string form
14
+ * (`-createdAt,name` or `sort[]=name`) and the already-structured
15
+ * `[{ field, direction }]` form the client builder's `build()` emits — the
16
+ * latter used to be filtered out entirely as "not a string", silently dropping
17
+ * the ordering.
18
+ */
19
+ export declare function parseSort(sort: unknown): SortItem[];
20
+ /**
21
+ * Parse a decoded request query object — e.g. AdonisJS `ctx.request.qs()` — into
22
+ * a structured {@link FilterInput}. Understands the Spatie / JSON:API shapes the
23
+ * `@adonis-agora/filter-client` builder emits:
24
+ *
25
+ * - `filter[status]=active` → equals
26
+ * - `filter[id]=1,2,3` / `filter[id][]=1&filter[id][]=2` → IN
27
+ * - `filter[age][gte]=18` → operator filter
28
+ * - `sort=-createdAt,name` → sort items
29
+ * - `distinct=afsc,base` / `distinct[]=afsc&distinct[]=base` → distinct fields
30
+ * - `groupByCount[field]=tag&groupByCount[limit]=20` → value enumeration
31
+ * - `search=term`, `page`/`size` (or `page[number]`/`page[size]`)
32
+ *
33
+ * It also accepts the structured shape the client builder's `build()` returns —
34
+ * `{ filter: { where: [...] }, sort: [{ field, direction }], paginate: { page, size } }`
35
+ * — so a POST search body can be handed straight in, and so OR/AND groups (which
36
+ * serialize to a top-level `where[0][field]=…`) survive the round trip.
37
+ * `include` is not consumed here: eager-loading is the caller's `preload` call.
38
+ *
39
+ * Pure reshape — no validation or allow-listing here; that happens in
40
+ * {@link applyFilter} against the {@link FilterConfig}.
41
+ */
42
+ export declare function parseFilterRequest(qs: Record<string, unknown>): FilterInput;
43
+ /**
44
+ * Parse the `groupByCount` block the client builder emits
45
+ * (`groupByCount[field]=tag&groupByCount[limit]=20&groupByCount[offset]=…&groupByCount[search]=…`).
46
+ * Returns `undefined` when no usable block is present. Pure reshape, like the rest of this
47
+ * module — field validation happens at execution, against the allow-list.
48
+ */
49
+ export declare function parseGroupByCount(value: unknown): GroupByCountRequest | undefined;
50
+ //# sourceMappingURL=parse_request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse_request.d.ts","sourceRoot":"","sources":["../../src/parse_request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAW7E,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,EAAE,CA4B7E;AA+BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAYzD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,EAAE,CAwBnD;AAqBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAkC3E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAWjF"}
@@ -0,0 +1,220 @@
1
+ function toInt(value) {
2
+ if (typeof value === 'number' && Number.isFinite(value))
3
+ return value;
4
+ if (typeof value === 'string' && value.trim() !== '') {
5
+ const n = Number(value);
6
+ if (Number.isFinite(n))
7
+ return n;
8
+ }
9
+ return undefined;
10
+ }
11
+ /** Map one `filter[field]=…` entry to `ColumnFilter[]` (Spatie/JSON:API shapes). */
12
+ export function toColumnFilters(field, value) {
13
+ // Array (`filter[id][]=1&filter[id][]=2`) → IN.
14
+ if (Array.isArray(value)) {
15
+ return [{ field, operator: 'in', value }];
16
+ }
17
+ // Operator object (`filter[age][gte]=18`) → one filter per operator key.
18
+ if (value != null && typeof value === 'object') {
19
+ return Object.entries(value).map(([operator, opValue]) => ({
20
+ field,
21
+ operator: operator,
22
+ value: opValue,
23
+ }));
24
+ }
25
+ // Comma-separated scalar (Spatie multi-value convention) → IN.
26
+ if (typeof value === 'string' && value.includes(',')) {
27
+ return [
28
+ {
29
+ field,
30
+ operator: 'in',
31
+ value: value
32
+ .split(',')
33
+ .map((s) => s.trim())
34
+ .filter((s) => s.length > 0),
35
+ },
36
+ ];
37
+ }
38
+ // Bare scalar → equals.
39
+ return [{ field, operator: 'equals', value }];
40
+ }
41
+ /**
42
+ * True when `value` is an already-structured `ColumnFilter[]` rather than the
43
+ * `filter[field]=…` shape `toColumnFilters` reshapes.
44
+ *
45
+ * Two wire forms produce it. A POST body carrying the client builder's
46
+ * `build()` output nests it under `filter.where`; a GET whose query string was
47
+ * serialized from OR/AND groups decodes to the same array of
48
+ * `{ field, operator, value }` records. Both used to fall through to the
49
+ * scalar path, where an array becomes a single `in` filter on a field literally
50
+ * named `where` — dropped by the allow-list, leaving the query unfiltered.
51
+ *
52
+ * A real column named `where` arrives as `filter[where]=x` (a string) or
53
+ * `filter[where][gte]=1` (an operator object), so neither is mistaken for this.
54
+ */
55
+ function isStructuredFilterList(value) {
56
+ return (Array.isArray(value) &&
57
+ value.length > 0 &&
58
+ value.every((entry) => entry != null &&
59
+ typeof entry === 'object' &&
60
+ !Array.isArray(entry) &&
61
+ typeof entry.field === 'string' &&
62
+ typeof entry.operator === 'string'));
63
+ }
64
+ /**
65
+ * Parse the `distinct` param into a de-duplicated list of field names. Accepts a
66
+ * comma-separated string (`distinct=afsc,base`) or a repeated/array form
67
+ * (`distinct[]=afsc&distinct[]=base`) — the shapes the client's `toQueryString()`
68
+ * and structured `build()` emit. Non-string entries are ignored.
69
+ */
70
+ export function parseDistinct(distinct) {
71
+ let raw = [];
72
+ if (typeof distinct === 'string')
73
+ raw = distinct.split(',');
74
+ else if (Array.isArray(distinct))
75
+ raw = distinct.filter((s) => typeof s === 'string');
76
+ const out = [];
77
+ for (const entry of raw) {
78
+ const field = entry.trim();
79
+ if (field.length > 0 && !out.includes(field))
80
+ out.push(field);
81
+ }
82
+ return out;
83
+ }
84
+ /**
85
+ * Parse the `sort` param into ordered {@link SortItem}s. Accepts the string form
86
+ * (`-createdAt,name` or `sort[]=name`) and the already-structured
87
+ * `[{ field, direction }]` form the client builder's `build()` emits — the
88
+ * latter used to be filtered out entirely as "not a string", silently dropping
89
+ * the ordering.
90
+ */
91
+ export function parseSort(sort) {
92
+ if (Array.isArray(sort) && sort.some((s) => s != null && typeof s === 'object')) {
93
+ const items = [];
94
+ for (const entry of sort) {
95
+ if (entry == null || typeof entry !== 'object')
96
+ continue;
97
+ const { field, direction } = entry;
98
+ if (typeof field !== 'string' || field.length === 0)
99
+ continue;
100
+ items.push({ field, direction: direction === 'desc' ? 'desc' : 'asc' });
101
+ }
102
+ return items;
103
+ }
104
+ let raw = [];
105
+ if (typeof sort === 'string')
106
+ raw = sort.split(',');
107
+ else if (Array.isArray(sort))
108
+ raw = sort.filter((s) => typeof s === 'string');
109
+ const items = [];
110
+ for (const entry of raw) {
111
+ const field = entry.trim();
112
+ if (field.length === 0)
113
+ continue;
114
+ if (field.startsWith('-'))
115
+ items.push({ field: field.slice(1), direction: 'desc' });
116
+ else
117
+ items.push({ field, direction: 'asc' });
118
+ }
119
+ return items;
120
+ }
121
+ function parsePagination(qs) {
122
+ let page = toInt(qs.page);
123
+ let size = toInt(qs.size);
124
+ // JSON:API nested form: page[number] / page[size].
125
+ if (qs.page != null && typeof qs.page === 'object' && !Array.isArray(qs.page)) {
126
+ const p = qs.page;
127
+ page = toInt(p.number) ?? page;
128
+ size = toInt(p.size) ?? size;
129
+ }
130
+ // The client builder's `build()` nests the same two numbers under `paginate`,
131
+ // which is what a POST search body carries.
132
+ if (qs.paginate != null && typeof qs.paginate === 'object' && !Array.isArray(qs.paginate)) {
133
+ const p = qs.paginate;
134
+ page = toInt(p.page) ?? page;
135
+ size = toInt(p.size) ?? size;
136
+ }
137
+ return { ...(page !== undefined && { page }), ...(size !== undefined && { size }) };
138
+ }
139
+ /**
140
+ * Parse a decoded request query object — e.g. AdonisJS `ctx.request.qs()` — into
141
+ * a structured {@link FilterInput}. Understands the Spatie / JSON:API shapes the
142
+ * `@adonis-agora/filter-client` builder emits:
143
+ *
144
+ * - `filter[status]=active` → equals
145
+ * - `filter[id]=1,2,3` / `filter[id][]=1&filter[id][]=2` → IN
146
+ * - `filter[age][gte]=18` → operator filter
147
+ * - `sort=-createdAt,name` → sort items
148
+ * - `distinct=afsc,base` / `distinct[]=afsc&distinct[]=base` → distinct fields
149
+ * - `groupByCount[field]=tag&groupByCount[limit]=20` → value enumeration
150
+ * - `search=term`, `page`/`size` (or `page[number]`/`page[size]`)
151
+ *
152
+ * It also accepts the structured shape the client builder's `build()` returns —
153
+ * `{ filter: { where: [...] }, sort: [{ field, direction }], paginate: { page, size } }`
154
+ * — so a POST search body can be handed straight in, and so OR/AND groups (which
155
+ * serialize to a top-level `where[0][field]=…`) survive the round trip.
156
+ * `include` is not consumed here: eager-loading is the caller's `preload` call.
157
+ *
158
+ * Pure reshape — no validation or allow-listing here; that happens in
159
+ * {@link applyFilter} against the {@link FilterConfig}.
160
+ */
161
+ export function parseFilterRequest(qs) {
162
+ const out = {};
163
+ const filters = [];
164
+ if (qs.filter != null && typeof qs.filter === 'object' && !Array.isArray(qs.filter)) {
165
+ for (const [field, value] of Object.entries(qs.filter)) {
166
+ if (field === 'where' && isStructuredFilterList(value)) {
167
+ filters.push(...value);
168
+ continue;
169
+ }
170
+ filters.push(...toColumnFilters(field, value));
171
+ }
172
+ }
173
+ // OR/AND groups serialize to a top-level `where[0][field]=…`, with no `filter`
174
+ // wrapper, so they decode beside `filter` rather than inside it.
175
+ if (isStructuredFilterList(qs.where))
176
+ filters.push(...qs.where);
177
+ if (filters.length > 0)
178
+ out.filters = filters;
179
+ const sort = parseSort(qs.sort);
180
+ if (sort.length > 0)
181
+ out.sort = sort;
182
+ const distinct = parseDistinct(qs.distinct);
183
+ if (distinct.length > 0)
184
+ out.distinct = distinct;
185
+ if (typeof qs.search === 'string' && qs.search.length > 0)
186
+ out.search = qs.search;
187
+ const { page, size } = parsePagination(qs);
188
+ if (page !== undefined)
189
+ out.page = page;
190
+ if (size !== undefined)
191
+ out.size = size;
192
+ const groupByCount = parseGroupByCount(qs.groupByCount);
193
+ if (groupByCount !== undefined)
194
+ out.groupByCount = groupByCount;
195
+ return out;
196
+ }
197
+ /**
198
+ * Parse the `groupByCount` block the client builder emits
199
+ * (`groupByCount[field]=tag&groupByCount[limit]=20&groupByCount[offset]=…&groupByCount[search]=…`).
200
+ * Returns `undefined` when no usable block is present. Pure reshape, like the rest of this
201
+ * module — field validation happens at execution, against the allow-list.
202
+ */
203
+ export function parseGroupByCount(value) {
204
+ if (value == null || typeof value !== 'object' || Array.isArray(value))
205
+ return undefined;
206
+ const block = value;
207
+ if (typeof block.field !== 'string' || block.field.trim() === '')
208
+ return undefined;
209
+ const out = { field: block.field };
210
+ const limit = toInt(block.limit);
211
+ if (limit !== undefined)
212
+ out.limit = limit;
213
+ const offset = toInt(block.offset);
214
+ if (offset !== undefined)
215
+ out.offset = offset;
216
+ if (typeof block.search === 'string' && block.search.length > 0)
217
+ out.search = block.search;
218
+ return out;
219
+ }
220
+ //# sourceMappingURL=parse_request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse_request.js","sourceRoot":"","sources":["../../src/parse_request.ts"],"names":[],"mappings":"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"}