@nestjs-filter-grammar/client-query-builder 0.1.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.
Files changed (43) hide show
  1. package/README.md +143 -0
  2. package/bin/filter-grammar.js +2 -0
  3. package/dist/cli/codegen.d.ts +18 -0
  4. package/dist/cli/codegen.d.ts.map +1 -0
  5. package/dist/cli/codegen.js +51 -0
  6. package/dist/cli/codegen.js.map +1 -0
  7. package/dist/cli/generate.d.ts +2 -0
  8. package/dist/cli/generate.d.ts.map +1 -0
  9. package/dist/cli/generate.js +95 -0
  10. package/dist/cli/generate.js.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +24 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/name-resolver.d.ts +2 -0
  16. package/dist/cli/name-resolver.d.ts.map +1 -0
  17. package/dist/cli/name-resolver.js +43 -0
  18. package/dist/cli/name-resolver.js.map +1 -0
  19. package/dist/combinators.d.ts +4 -0
  20. package/dist/combinators.d.ts.map +1 -0
  21. package/dist/combinators.js +12 -0
  22. package/dist/combinators.js.map +1 -0
  23. package/dist/condition.d.ts +18 -0
  24. package/dist/condition.d.ts.map +1 -0
  25. package/dist/condition.js +39 -0
  26. package/dist/condition.js.map +1 -0
  27. package/dist/field.d.ts +2 -0
  28. package/dist/field.d.ts.map +1 -0
  29. package/dist/field.js +22 -0
  30. package/dist/field.js.map +1 -0
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +18 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/serialize.d.ts +4 -0
  36. package/dist/serialize.d.ts.map +1 -0
  37. package/dist/serialize.js +44 -0
  38. package/dist/serialize.js.map +1 -0
  39. package/dist/sort.d.ts +13 -0
  40. package/dist/sort.d.ts.map +1 -0
  41. package/dist/sort.js +27 -0
  42. package/dist/sort.js.map +1 -0
  43. package/package.json +28 -0
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @nestjs-filter-grammar/client-query-builder
2
+
3
+ Type-safe client-side filter and sort query builder for APIs using `@nestjs-filter-grammar`. Generates typed filter/sort objects from your API's OpenAPI spec.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @nestjs-filter-grammar/client-query-builder
9
+ ```
10
+
11
+ ## Code Generation
12
+
13
+ Generate typed filter/sort builders from an OpenAPI spec that contains `x-filter-grammar` extensions (automatically added by the `@Filter()` decorator when `@nestjs/swagger` is installed).
14
+
15
+ ```bash
16
+ npx filter-grammar generate ./openapi.json -o ./src/generated
17
+ ```
18
+
19
+ Options:
20
+ - `-o, --output <dir>` — Output directory (default: `./src/generated`)
21
+
22
+ This generates one file per endpoint with `FilterX` and `SortX` objects, plus a barrel `index.ts`.
23
+
24
+ ### Generated Output
25
+
26
+ For a `GET /users` endpoint:
27
+
28
+ ```typescript
29
+ // generated/users.ts
30
+ import { field, sortField } from '@nestjs-filter-grammar/client-query-builder';
31
+
32
+ export const FilterUsers = {
33
+ name: field<string>('name', ['=', '!=', '*~']),
34
+ status: field<'active' | 'inactive' | 'pending'>('status', ['=', '!=']),
35
+ age: field<number>('age', ['>=', '<=']),
36
+ } as const;
37
+
38
+ export const SortUsers = {
39
+ name: sortField('name'),
40
+ age: sortField('age'),
41
+ } as const;
42
+ ```
43
+
44
+ ## Runtime API
45
+
46
+ ### Filter Conditions
47
+
48
+ ```typescript
49
+ import { FilterUsers } from './generated';
50
+
51
+ // Simple equality
52
+ FilterUsers.name.eq('John').build() // "name=John"
53
+
54
+ // Multi-value (IN)
55
+ FilterUsers.status.eq('active', 'pending').build() // "status=active,pending"
56
+
57
+ // Null
58
+ FilterUsers.email.eq(null).build() // "email=null"
59
+
60
+ // Comparison
61
+ FilterUsers.age.gte(18).build() // "age>=18"
62
+
63
+ // Case-insensitive contains
64
+ FilterUsers.name.iContains('john').build() // "name*~john"
65
+ ```
66
+
67
+ ### Combining with `and()` / `or()`
68
+
69
+ ```typescript
70
+ import { and, or } from '@nestjs-filter-grammar/client-query-builder';
71
+
72
+ // AND
73
+ and(
74
+ FilterUsers.name.eq('John'),
75
+ FilterUsers.age.gte(18),
76
+ ).build()
77
+ // → "name=John;age>=18"
78
+
79
+ // OR
80
+ or(
81
+ FilterUsers.status.eq('active'),
82
+ FilterUsers.status.eq('pending'),
83
+ ).build()
84
+ // → "status=active|status=pending"
85
+
86
+ // Nested — automatically parenthesized
87
+ and(
88
+ FilterUsers.name.eq('John'),
89
+ or(
90
+ FilterUsers.status.eq('active'),
91
+ FilterUsers.status.eq('pending'),
92
+ ),
93
+ ).build()
94
+ // → "name=John;(status=active|status=pending)"
95
+ ```
96
+
97
+ ### Sort
98
+
99
+ ```typescript
100
+ import { sort } from '@nestjs-filter-grammar/client-query-builder';
101
+ import { SortUsers } from './generated';
102
+
103
+ SortUsers.name.asc().build() // "+name"
104
+ SortUsers.age.desc().build() // "-age"
105
+
106
+ // Multiple
107
+ sort(
108
+ SortUsers.name.asc(),
109
+ SortUsers.age.desc(),
110
+ ).build()
111
+ // → "+name,-age"
112
+ ```
113
+
114
+ ### Full Example
115
+
116
+ ```typescript
117
+ import { and, or, sort } from '@nestjs-filter-grammar/client-query-builder';
118
+ import { FilterUsers, SortUsers } from './generated';
119
+
120
+ const filter = and(
121
+ or(
122
+ FilterUsers.status.eq('active'),
123
+ FilterUsers.status.eq('pending'),
124
+ ),
125
+ FilterUsers.age.gte(18),
126
+ ).build();
127
+
128
+ const sortStr = sort(
129
+ SortUsers.name.asc(),
130
+ SortUsers.age.desc(),
131
+ ).build();
132
+
133
+ const response = await fetch(`/api/users?filter=${filter}&sort=${sortStr}`);
134
+ ```
135
+
136
+ ## Value Handling
137
+
138
+ - Plain strings pass through: `name=John`
139
+ - Values with special characters are auto-quoted: `name="Smith, Jr."`
140
+ - Quotes and backslashes inside values are escaped: `name="say \"hi\""`
141
+ - Numbers serialize as-is: `age>=18`
142
+ - Booleans serialize as-is: `active=true`
143
+ - Null: `email=null`
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../dist/cli/index.js');
@@ -0,0 +1,18 @@
1
+ export interface FieldDef {
2
+ operators: string[];
3
+ type: 'string' | 'number' | 'boolean' | 'enum';
4
+ values?: (string | number)[];
5
+ }
6
+ export interface EndpointDef {
7
+ fields: Record<string, FieldDef>;
8
+ sortable: string[];
9
+ }
10
+ export declare function generateFile(name: string, endpoint: EndpointDef): string;
11
+ export interface BarrelEntry {
12
+ filename: string;
13
+ name: string;
14
+ hasFilter: boolean;
15
+ hasSort: boolean;
16
+ }
17
+ export declare function generateBarrel(entries: BarrelEntry[]): string;
18
+ //# sourceMappingURL=codegen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/cli/codegen.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IAC/C,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAWD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,MAAM,CAyBxE;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAY7D"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFile = generateFile;
4
+ exports.generateBarrel = generateBarrel;
5
+ function typeToGeneric(fieldDef) {
6
+ if (fieldDef.type === 'enum' && fieldDef.values) {
7
+ return fieldDef.values
8
+ .map((v) => (typeof v === 'string' ? `'${v}'` : String(v)))
9
+ .join(' | ');
10
+ }
11
+ return fieldDef.type;
12
+ }
13
+ function generateFile(name, endpoint) {
14
+ const lines = [
15
+ "import { field, sortField } from '@nestjs-filter-grammar/client-query-builder';",
16
+ '',
17
+ ];
18
+ lines.push(`export const Filter${name} = {`);
19
+ for (const [fieldName, def] of Object.entries(endpoint.fields)) {
20
+ const generic = typeToGeneric(def);
21
+ const operators = JSON.stringify(def.operators);
22
+ lines.push(` ${fieldName}: field<${generic}>('${fieldName}', ${operators}),`);
23
+ }
24
+ lines.push('} as const;');
25
+ if (endpoint.sortable.length > 0) {
26
+ lines.push('');
27
+ lines.push(`export const Sort${name} = {`);
28
+ for (const fieldName of endpoint.sortable) {
29
+ lines.push(` ${fieldName}: sortField('${fieldName}'),`);
30
+ }
31
+ lines.push('} as const;');
32
+ }
33
+ lines.push('');
34
+ return lines.join('\n');
35
+ }
36
+ function generateBarrel(entries) {
37
+ const lines = [];
38
+ for (const entry of entries) {
39
+ const exports = [];
40
+ if (entry.hasFilter)
41
+ exports.push(`Filter${entry.name}`);
42
+ if (entry.hasSort)
43
+ exports.push(`Sort${entry.name}`);
44
+ if (exports.length > 0) {
45
+ lines.push(`export { ${exports.join(', ')} } from './${entry.filename}';`);
46
+ }
47
+ }
48
+ lines.push('');
49
+ return lines.join('\n');
50
+ }
51
+ //# sourceMappingURL=codegen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.js","sourceRoot":"","sources":["../../src/cli/codegen.ts"],"names":[],"mappings":";;AAoBA,oCAyBC;AASD,wCAYC;AAvDD,SAAS,aAAa,CAAC,QAAkB;IACvC,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAChD,OAAO,QAAQ,CAAC,MAAM;aACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;aAC1D,IAAI,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC;AAED,SAAgB,YAAY,CAAC,IAAY,EAAE,QAAqB;IAC9D,MAAM,KAAK,GAAa;QACtB,iFAAiF;QACjF,EAAE;KACH,CAAC;IAEF,KAAK,CAAC,IAAI,CAAC,sBAAsB,IAAI,MAAM,CAAC,CAAC;IAC7C,KAAK,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,WAAW,OAAO,MAAM,SAAS,MAAM,SAAS,IAAI,CAAC,CAAC;IACjF,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAE1B,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,oBAAoB,IAAI,MAAM,CAAC,CAAC;QAC3C,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,gBAAgB,SAAS,KAAK,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AASD,SAAgB,cAAc,CAAC,OAAsB;IACnD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,IAAI,KAAK,CAAC,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function generate(specPath: string, outputDir: string): void;
2
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AA+BA,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAoDlE"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generate = generate;
37
+ const fs_1 = require("fs");
38
+ const path_1 = require("path");
39
+ const yaml = __importStar(require("js-yaml"));
40
+ const name_resolver_1 = require("./name-resolver");
41
+ const codegen_1 = require("./codegen");
42
+ function loadSpec(filePath) {
43
+ if (!(0, fs_1.existsSync)(filePath)) {
44
+ throw new Error(`File not found: ${filePath}`);
45
+ }
46
+ const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
47
+ const ext = (0, path_1.extname)(filePath).toLowerCase();
48
+ if (ext === '.yaml' || ext === '.yml') {
49
+ return yaml.load(content);
50
+ }
51
+ return JSON.parse(content);
52
+ }
53
+ function generate(specPath, outputDir) {
54
+ const spec = loadSpec(specPath);
55
+ if (!spec.paths) {
56
+ console.warn('No paths found in spec');
57
+ return;
58
+ }
59
+ const endpoints = new Map();
60
+ for (const [path, methods] of Object.entries(spec.paths)) {
61
+ for (const operation of Object.values(methods)) {
62
+ const ext = operation['x-filter-grammar'];
63
+ if (ext) {
64
+ endpoints.set(path, {
65
+ fields: ext.fields,
66
+ sortable: ext.sortable,
67
+ });
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ if (endpoints.size === 0) {
73
+ console.warn('No x-filter-grammar extensions found in spec');
74
+ return;
75
+ }
76
+ const names = (0, name_resolver_1.resolveNames)([...endpoints.keys()]);
77
+ (0, fs_1.mkdirSync)(outputDir, { recursive: true });
78
+ const barrelEntries = [];
79
+ for (const [path, endpoint] of endpoints) {
80
+ const name = names.get(path);
81
+ const filename = name.charAt(0).toLowerCase() + name.slice(1);
82
+ const content = (0, codegen_1.generateFile)(name, endpoint);
83
+ (0, fs_1.writeFileSync)((0, path_1.join)(outputDir, `${filename}.ts`), content);
84
+ barrelEntries.push({
85
+ filename,
86
+ name,
87
+ hasFilter: Object.keys(endpoint.fields).length > 0,
88
+ hasSort: endpoint.sortable.length > 0,
89
+ });
90
+ }
91
+ const barrelContent = (0, codegen_1.generateBarrel)(barrelEntries);
92
+ (0, fs_1.writeFileSync)((0, path_1.join)(outputDir, 'index.ts'), barrelContent);
93
+ console.log(`Generated ${barrelEntries.length} file(s) in ${outputDir}`);
94
+ }
95
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const commander_1 = require("commander");
4
+ const generate_1 = require("./generate");
5
+ const program = new commander_1.Command();
6
+ program
7
+ .name('filter-grammar')
8
+ .description('Generate type-safe filter/sort query builders from OpenAPI specs');
9
+ program
10
+ .command('generate')
11
+ .description('Generate TypeScript filter/sort builders from an OpenAPI spec')
12
+ .argument('<spec>', 'Path to OpenAPI JSON or YAML file')
13
+ .option('-o, --output <dir>', 'Output directory', './src/generated')
14
+ .action((spec, opts) => {
15
+ try {
16
+ (0, generate_1.generate)(spec, opts.output);
17
+ }
18
+ catch (err) {
19
+ console.error(`Error: ${err.message}`);
20
+ process.exit(1);
21
+ }
22
+ });
23
+ program.parse();
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,yCAAsC;AAEtC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,gBAAgB,CAAC;KACtB,WAAW,CAAC,kEAAkE,CAAC,CAAC;AAEnF,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,+DAA+D,CAAC;KAC5E,QAAQ,CAAC,QAAQ,EAAE,mCAAmC,CAAC;KACvD,MAAM,CAAC,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,CAAC;KACnE,MAAM,CAAC,CAAC,IAAY,EAAE,IAAwB,EAAE,EAAE;IACjD,IAAI,CAAC;QACH,IAAA,mBAAQ,EAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,UAAW,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function resolveNames(paths: string[]): Map<string, string>;
2
+ //# sourceMappingURL=name-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-resolver.d.ts","sourceRoot":"","sources":["../../src/cli/name-resolver.ts"],"names":[],"mappings":"AAoBA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBjE"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveNames = resolveNames;
4
+ function pathToName(path) {
5
+ const segments = path
6
+ .split('/')
7
+ .filter((s) => s && !s.startsWith('{') && !isCommonPrefix(s));
8
+ if (segments.length === 0)
9
+ return 'Root';
10
+ // Use the last meaningful segment as the resource name
11
+ const last = segments[segments.length - 1];
12
+ return last.split('-').map(capitalize).join('');
13
+ }
14
+ function isCommonPrefix(segment) {
15
+ return /^(api|v\d+)$/i.test(segment);
16
+ }
17
+ function capitalize(s) {
18
+ return s.charAt(0).toUpperCase() + s.slice(1);
19
+ }
20
+ function resolveNames(paths) {
21
+ const rawNames = new Map();
22
+ for (const path of paths) {
23
+ rawNames.set(path, pathToName(path));
24
+ }
25
+ const nameCounts = new Map();
26
+ for (const name of rawNames.values()) {
27
+ nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
28
+ }
29
+ const result = new Map();
30
+ const usedNames = new Map();
31
+ for (const [path, name] of rawNames) {
32
+ if ((nameCounts.get(name) ?? 0) > 1) {
33
+ const count = usedNames.get(name) ?? 0;
34
+ result.set(path, count === 0 ? name : `${name}Fields`);
35
+ usedNames.set(name, count + 1);
36
+ }
37
+ else {
38
+ result.set(path, name);
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+ //# sourceMappingURL=name-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-resolver.js","sourceRoot":"","sources":["../../src/cli/name-resolver.ts"],"names":[],"mappings":";;AAoBA,oCAyBC;AA7CD,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,QAAQ,GAAG,IAAI;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAEzC,uDAAuD;IACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,YAAY,CAAC,KAAe;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QACrC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC;YACvD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Buildable, CompoundCondition } from './condition';
2
+ export declare function and(...conditions: Buildable[]): CompoundCondition;
3
+ export declare function or(...conditions: Buildable[]): CompoundCondition;
4
+ //# sourceMappingURL=combinators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combinators.d.ts","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3D,wBAAgB,GAAG,CAAC,GAAG,UAAU,EAAE,SAAS,EAAE,GAAG,iBAAiB,CAEjE;AAED,wBAAgB,EAAE,CAAC,GAAG,UAAU,EAAE,SAAS,EAAE,GAAG,iBAAiB,CAEhE"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.and = and;
4
+ exports.or = or;
5
+ const condition_1 = require("./condition");
6
+ function and(...conditions) {
7
+ return new condition_1.CompoundCondition('AND', conditions);
8
+ }
9
+ function or(...conditions) {
10
+ return new condition_1.CompoundCondition('OR', conditions);
11
+ }
12
+ //# sourceMappingURL=combinators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combinators.js","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":";;AAEA,kBAEC;AAED,gBAEC;AARD,2CAA2D;AAE3D,SAAgB,GAAG,CAAC,GAAG,UAAuB;IAC5C,OAAO,IAAI,6BAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,SAAgB,EAAE,CAAC,GAAG,UAAuB;IAC3C,OAAO,IAAI,6BAAiB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface Buildable {
2
+ build(): string;
3
+ readonly compoundType?: 'AND' | 'OR';
4
+ }
5
+ export declare class Condition implements Buildable {
6
+ private readonly fieldName;
7
+ private readonly operator;
8
+ private readonly values;
9
+ constructor(fieldName: string, operator: string, values: (string | number | boolean | null)[]);
10
+ build(): string;
11
+ }
12
+ export declare class CompoundCondition implements Buildable {
13
+ private readonly children;
14
+ readonly compoundType: 'AND' | 'OR';
15
+ constructor(type: 'AND' | 'OR', children: Buildable[]);
16
+ build(): string;
17
+ }
18
+ //# sourceMappingURL=condition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,MAAM,CAAC;IAChB,QAAQ,CAAC,YAAY,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACtC;AAED,qBAAa,SAAU,YAAW,SAAS;IAEvC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE;IAG/D,KAAK,IAAI,MAAM;CAIhB;AAED,qBAAa,iBAAkB,YAAW,SAAS;IAK/C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,QAAQ,CAAC,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;gBAGlC,IAAI,EAAE,KAAK,GAAG,IAAI,EACD,QAAQ,EAAE,SAAS,EAAE;IAKxC,KAAK,IAAI,MAAM;CAiBhB"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompoundCondition = exports.Condition = void 0;
4
+ const serialize_1 = require("./serialize");
5
+ class Condition {
6
+ constructor(fieldName, operator, values) {
7
+ this.fieldName = fieldName;
8
+ this.operator = operator;
9
+ this.values = values;
10
+ }
11
+ build() {
12
+ const serializedValues = this.values.map(serialize_1.serializeValue).join(',');
13
+ return `${this.fieldName}${this.operator}${serializedValues}`;
14
+ }
15
+ }
16
+ exports.Condition = Condition;
17
+ class CompoundCondition {
18
+ constructor(type, children) {
19
+ this.children = children;
20
+ this.compoundType = type;
21
+ }
22
+ build() {
23
+ if (this.children.length === 1) {
24
+ return this.children[0].build();
25
+ }
26
+ const separator = this.compoundType === 'AND' ? ';' : '|';
27
+ return this.children
28
+ .map((child) => {
29
+ const built = child.build();
30
+ if (child.compoundType && child.compoundType !== this.compoundType) {
31
+ return `(${built})`;
32
+ }
33
+ return built;
34
+ })
35
+ .join(separator);
36
+ }
37
+ }
38
+ exports.CompoundCondition = CompoundCondition;
39
+ //# sourceMappingURL=condition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition.js","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAO7C,MAAa,SAAS;IACpB,YACmB,SAAiB,EACjB,QAAgB,EAChB,MAA4C;QAF5C,cAAS,GAAT,SAAS,CAAQ;QACjB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,WAAM,GAAN,MAAM,CAAsC;IAC5D,CAAC;IAEJ,KAAK;QACH,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,0BAAc,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnE,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IAChE,CAAC;CACF;AAXD,8BAWC;AAED,MAAa,iBAAiB;IAG5B,YACE,IAAkB,EACD,QAAqB;QAArB,aAAQ,GAAR,QAAQ,CAAa;QAEtC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAE1D,OAAO,IAAI,CAAC,QAAQ;aACjB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACb,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnE,OAAO,IAAI,KAAK,GAAG,CAAC;YACtB,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;aACD,IAAI,CAAC,SAAS,CAAC,CAAC;IACrB,CAAC;CACF;AA3BD,8CA2BC"}
@@ -0,0 +1,2 @@
1
+ export declare function field<T>(name: string, operatorSymbols: string[]): Record<string, Function>;
2
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAKA,wBAAgB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAiB1F"}
package/dist/field.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.field = field;
4
+ const condition_1 = require("./condition");
5
+ const serialize_1 = require("./serialize");
6
+ const MULTI_VALUE_METHODS = new Set(['eq', 'neq', 'iEq', 'iNeq']);
7
+ function field(name, operatorSymbols) {
8
+ const methods = {};
9
+ for (const symbol of operatorSymbols) {
10
+ const methodName = (0, serialize_1.methodNameForSymbol)(symbol);
11
+ if (!methodName)
12
+ continue;
13
+ if (MULTI_VALUE_METHODS.has(methodName)) {
14
+ methods[methodName] = (...values) => new condition_1.Condition(name, symbol, values);
15
+ }
16
+ else {
17
+ methods[methodName] = (value) => new condition_1.Condition(name, symbol, [value]);
18
+ }
19
+ }
20
+ return methods;
21
+ }
22
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":";;AAKA,sBAiBC;AAtBD,2CAAwC;AACxC,2CAAkD;AAElD,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAElE,SAAgB,KAAK,CAAI,IAAY,EAAE,eAAyB;IAC9D,MAAM,OAAO,GAA6B,EAAE,CAAC;IAE7C,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,IAAA,+BAAmB,EAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,IAAI,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,MAAoB,EAAE,EAAE,CAChD,IAAI,qBAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAA8C,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,KAAQ,EAAE,EAAE,CACjC,IAAI,qBAAS,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAyC,CAAC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { Condition, CompoundCondition } from './condition';
2
+ export type { Buildable } from './condition';
3
+ export { and, or } from './combinators';
4
+ export { field } from './field';
5
+ export { sortField, sort, SortExpression } from './sort';
6
+ export { serializeValue } from './serialize';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeValue = exports.SortExpression = exports.sort = exports.sortField = exports.field = exports.or = exports.and = exports.CompoundCondition = exports.Condition = void 0;
4
+ var condition_1 = require("./condition");
5
+ Object.defineProperty(exports, "Condition", { enumerable: true, get: function () { return condition_1.Condition; } });
6
+ Object.defineProperty(exports, "CompoundCondition", { enumerable: true, get: function () { return condition_1.CompoundCondition; } });
7
+ var combinators_1 = require("./combinators");
8
+ Object.defineProperty(exports, "and", { enumerable: true, get: function () { return combinators_1.and; } });
9
+ Object.defineProperty(exports, "or", { enumerable: true, get: function () { return combinators_1.or; } });
10
+ var field_1 = require("./field");
11
+ Object.defineProperty(exports, "field", { enumerable: true, get: function () { return field_1.field; } });
12
+ var sort_1 = require("./sort");
13
+ Object.defineProperty(exports, "sortField", { enumerable: true, get: function () { return sort_1.sortField; } });
14
+ Object.defineProperty(exports, "sort", { enumerable: true, get: function () { return sort_1.sort; } });
15
+ Object.defineProperty(exports, "SortExpression", { enumerable: true, get: function () { return sort_1.SortExpression; } });
16
+ var serialize_1 = require("./serialize");
17
+ Object.defineProperty(exports, "serializeValue", { enumerable: true, get: function () { return serialize_1.serializeValue; } });
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA2D;AAAlD,sGAAA,SAAS,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAErC,6CAAwC;AAA/B,kGAAA,GAAG,OAAA;AAAE,iGAAA,EAAE,OAAA;AAChB,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AACd,+BAAyD;AAAhD,iGAAA,SAAS,OAAA;AAAE,4FAAA,IAAI,OAAA;AAAE,sGAAA,cAAc,OAAA;AACxC,yCAA6C;AAApC,2GAAA,cAAc,OAAA"}
@@ -0,0 +1,4 @@
1
+ export declare function serializeValue(value: string | number | boolean | null): string;
2
+ export declare function operatorSymbol(methodName: string): string;
3
+ export declare function methodNameForSymbol(symbol: string): string;
4
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAS9E;AAmBD,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAOD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeValue = serializeValue;
4
+ exports.operatorSymbol = operatorSymbol;
5
+ exports.methodNameForSymbol = methodNameForSymbol;
6
+ const NEEDS_QUOTING = /[,"|;!=><^$*~()\\\s]/;
7
+ function serializeValue(value) {
8
+ if (value === null)
9
+ return 'null';
10
+ if (typeof value === 'number' || typeof value === 'boolean')
11
+ return String(value);
12
+ if (NEEDS_QUOTING.test(value)) {
13
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
14
+ return `"${escaped}"`;
15
+ }
16
+ return value;
17
+ }
18
+ const OPERATOR_SYMBOLS = {
19
+ eq: '=',
20
+ neq: '!=',
21
+ gt: '>',
22
+ lt: '<',
23
+ gte: '>=',
24
+ lte: '<=',
25
+ iEq: '~',
26
+ iNeq: '!~',
27
+ startsWith: '^=',
28
+ endsWith: '$=',
29
+ contains: '*=',
30
+ iStartsWith: '^~',
31
+ iEndsWith: '$~',
32
+ iContains: '*~',
33
+ };
34
+ function operatorSymbol(methodName) {
35
+ return OPERATOR_SYMBOLS[methodName];
36
+ }
37
+ const SYMBOL_TO_METHOD = {};
38
+ for (const [method, symbol] of Object.entries(OPERATOR_SYMBOLS)) {
39
+ SYMBOL_TO_METHOD[symbol] = method;
40
+ }
41
+ function methodNameForSymbol(symbol) {
42
+ return SYMBOL_TO_METHOD[symbol];
43
+ }
44
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":";;AAEA,wCASC;AAmBD,wCAEC;AAOD,kDAEC;AAzCD,MAAM,aAAa,GAAG,sBAAsB,CAAC;AAE7C,SAAgB,cAAc,CAAC,KAAuC;IACpE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAElF,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClE,OAAO,IAAI,OAAO,GAAG,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,gBAAgB,GAA2B;IAC/C,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,IAAI;IACT,EAAE,EAAE,GAAG;IACP,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,SAAgB,cAAc,CAAC,UAAkB;IAC/C,OAAO,gBAAgB,CAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,gBAAgB,GAA2B,EAAE,CAAC;AACpD,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;IAChE,gBAAgB,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AACpC,CAAC;AAED,SAAgB,mBAAmB,CAAC,MAAc;IAChD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC"}
package/dist/sort.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { Buildable } from './condition';
2
+ export declare class SortExpression implements Buildable {
3
+ private readonly fieldName;
4
+ private readonly direction;
5
+ constructor(fieldName: string, direction: '+' | '-');
6
+ build(): string;
7
+ }
8
+ export declare function sortField(name: string): {
9
+ asc(): SortExpression;
10
+ desc(): SortExpression;
11
+ };
12
+ export declare function sort(...expressions: SortExpression[]): Buildable;
13
+ //# sourceMappingURL=sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../src/sort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qBAAa,cAAe,YAAW,SAAS;IAE5C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,GAAG,GAAG,GAAG;IAGvC,KAAK,IAAI,MAAM;CAGhB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,IAAI,cAAc,CAAC;IAAC,IAAI,IAAI,cAAc,CAAA;CAAE,CAKzF;AAED,wBAAgB,IAAI,CAAC,GAAG,WAAW,EAAE,cAAc,EAAE,GAAG,SAAS,CAIhE"}
package/dist/sort.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SortExpression = void 0;
4
+ exports.sortField = sortField;
5
+ exports.sort = sort;
6
+ class SortExpression {
7
+ constructor(fieldName, direction) {
8
+ this.fieldName = fieldName;
9
+ this.direction = direction;
10
+ }
11
+ build() {
12
+ return `${this.direction}${this.fieldName}`;
13
+ }
14
+ }
15
+ exports.SortExpression = SortExpression;
16
+ function sortField(name) {
17
+ return {
18
+ asc: () => new SortExpression(name, '+'),
19
+ desc: () => new SortExpression(name, '-'),
20
+ };
21
+ }
22
+ function sort(...expressions) {
23
+ return {
24
+ build: () => expressions.map((e) => e.build()).join(','),
25
+ };
26
+ }
27
+ //# sourceMappingURL=sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../src/sort.ts"],"names":[],"mappings":";;;AAaA,8BAKC;AAED,oBAIC;AAtBD,MAAa,cAAc;IACzB,YACmB,SAAiB,EACjB,SAAoB;QADpB,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,KAAK;QACH,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAC9C,CAAC;CACF;AATD,wCASC;AAED,SAAgB,SAAS,CAAC,IAAY;IACpC,OAAO;QACL,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC;QACxC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,SAAgB,IAAI,CAAC,GAAG,WAA6B;IACnD,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;KACzD,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@nestjs-filter-grammar/client-query-builder",
3
+ "version": "0.1.0",
4
+ "description": "Type-safe client-side filter/sort query builder with OpenAPI codegen for @nestjs-filter-grammar",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "filter-grammar": "./bin/filter-grammar.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p tsconfig.build.json",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "typecheck": "tsc --noEmit"
15
+ },
16
+ "dependencies": {
17
+ "commander": "^12.0.0",
18
+ "js-yaml": "^4.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/js-yaml": "^4.0.0",
22
+ "@types/node": "^20.19.37"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "bin"
27
+ ]
28
+ }