@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.
- package/README.md +143 -0
- package/bin/filter-grammar.js +2 -0
- package/dist/cli/codegen.d.ts +18 -0
- package/dist/cli/codegen.d.ts.map +1 -0
- package/dist/cli/codegen.js +51 -0
- package/dist/cli/codegen.js.map +1 -0
- package/dist/cli/generate.d.ts +2 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +95 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +24 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/name-resolver.d.ts +2 -0
- package/dist/cli/name-resolver.d.ts.map +1 -0
- package/dist/cli/name-resolver.js +43 -0
- package/dist/cli/name-resolver.js.map +1 -0
- package/dist/combinators.d.ts +4 -0
- package/dist/combinators.d.ts.map +1 -0
- package/dist/combinators.js +12 -0
- package/dist/combinators.js.map +1 -0
- package/dist/condition.d.ts +18 -0
- package/dist/condition.d.ts.map +1 -0
- package/dist/condition.js +39 -0
- package/dist/condition.js.map +1 -0
- package/dist/field.d.ts +2 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +22 -0
- package/dist/field.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/serialize.d.ts +4 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +44 -0
- package/dist/serialize.js.map +1 -0
- package/dist/sort.d.ts +13 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/sort.js +27 -0
- package/dist/sort.js.map +1 -0
- 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,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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/field.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/sort.js.map
ADDED
|
@@ -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
|
+
}
|