@eienjs/adonisjs-api-query 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +138 -0
  2. package/build/configure.d.ts +1 -5
  3. package/build/configure.js +1 -2
  4. package/build/index.d.ts +9 -3
  5. package/build/index.js +8 -2
  6. package/build/providers/api_query_provider.d.ts +20 -13
  7. package/build/providers/api_query_provider.js +7 -3
  8. package/build/src/allowed_filter.d.ts +14 -10
  9. package/build/src/allowed_filter.js +32 -10
  10. package/build/src/allowed_include.d.ts +6 -11
  11. package/build/src/allowed_include.js +14 -15
  12. package/build/src/allowed_sort.d.ts +2 -6
  13. package/build/src/allowed_sort.js +1 -2
  14. package/build/src/api_query_builder_request.d.ts +33 -12
  15. package/build/src/api_query_builder_request.js +119 -41
  16. package/build/src/base_filterable.d.ts +45 -0
  17. package/build/src/base_filterable.js +172 -0
  18. package/build/src/bindings/api_query_request.js +3 -3
  19. package/build/src/bindings/filterable_query.js +51 -0
  20. package/build/src/bindings/filters_query.js +21 -23
  21. package/build/src/bindings/includes_query.js +32 -33
  22. package/build/src/bindings/sorts_query.js +20 -23
  23. package/build/src/bindings/validation_query.js +27 -0
  24. package/build/src/decorators/filterable.d.ts +20 -0
  25. package/build/src/decorators/filterable.js +24 -0
  26. package/build/src/define_config.d.ts +1 -5
  27. package/build/src/define_config.js +3 -3
  28. package/build/src/enums/filter_operator.d.ts +2 -5
  29. package/build/src/enums/filter_operator.js +1 -2
  30. package/build/src/enums/sort_direction.d.ts +2 -5
  31. package/build/src/enums/sort_direction.js +1 -2
  32. package/build/src/exceptions/allowed_fields_must_be_called_before_allowed_includes.d.ts +4 -8
  33. package/build/src/exceptions/allowed_fields_must_be_called_before_allowed_includes.js +3 -4
  34. package/build/src/exceptions/filterable_not_configured.d.ts +5 -0
  35. package/build/src/exceptions/filterable_not_configured.js +12 -0
  36. package/build/src/exceptions/invalid_append_query.d.ts +4 -6
  37. package/build/src/exceptions/invalid_append_query.js +5 -2
  38. package/build/src/exceptions/invalid_direction.d.ts +2 -6
  39. package/build/src/exceptions/invalid_direction.js +1 -2
  40. package/build/src/exceptions/invalid_field_query.d.ts +7 -0
  41. package/build/src/exceptions/invalid_field_query.js +17 -0
  42. package/build/src/exceptions/invalid_filter_query.d.ts +2 -6
  43. package/build/src/exceptions/invalid_filter_query.js +1 -2
  44. package/build/src/exceptions/invalid_filter_value.d.ts +2 -6
  45. package/build/src/exceptions/invalid_filter_value.js +1 -2
  46. package/build/src/exceptions/invalid_include_query.d.ts +2 -6
  47. package/build/src/exceptions/invalid_include_query.js +1 -2
  48. package/build/src/exceptions/invalid_query.d.ts +2 -6
  49. package/build/src/exceptions/invalid_query.js +1 -2
  50. package/build/src/exceptions/invalid_sort_query.d.ts +2 -6
  51. package/build/src/exceptions/invalid_sort_query.js +1 -2
  52. package/build/src/exceptions/unknown_included_fields_query.d.ts +2 -6
  53. package/build/src/exceptions/unknown_included_fields_query.js +1 -2
  54. package/build/src/exceptions/vine_not_installed.d.ts +5 -0
  55. package/build/src/exceptions/vine_not_installed.js +12 -0
  56. package/build/src/filters/filters_begin_with_strict.d.ts +2 -6
  57. package/build/src/filters/filters_begin_with_strict.js +2 -3
  58. package/build/src/filters/filters_callback.d.ts +2 -6
  59. package/build/src/filters/filters_callback.js +1 -2
  60. package/build/src/filters/filters_ends_with_strict.d.ts +2 -6
  61. package/build/src/filters/filters_ends_with_strict.js +2 -3
  62. package/build/src/filters/filters_exact.d.ts +2 -6
  63. package/build/src/filters/filters_exact.js +15 -12
  64. package/build/src/filters/filters_operator.d.ts +2 -6
  65. package/build/src/filters/filters_operator.js +9 -10
  66. package/build/src/filters/filters_partial.d.ts +2 -8
  67. package/build/src/filters/filters_partial.js +7 -9
  68. package/build/src/filters/filters_scope.d.ts +12 -0
  69. package/build/src/filters/filters_scope.js +98 -0
  70. package/build/src/filters/filters_trashed.d.ts +2 -6
  71. package/build/src/filters/filters_trashed.js +1 -2
  72. package/build/src/includes/included_callback.d.ts +2 -6
  73. package/build/src/includes/included_callback.js +1 -2
  74. package/build/src/includes/included_count.d.ts +2 -6
  75. package/build/src/includes/included_count.js +2 -2
  76. package/build/src/includes/included_relationship.d.ts +4 -8
  77. package/build/src/includes/included_relationship.js +19 -22
  78. package/build/src/sorts/sorts_callback.d.ts +2 -6
  79. package/build/src/sorts/sorts_callback.js +1 -2
  80. package/build/src/sorts/sorts_field.d.ts +2 -6
  81. package/build/src/sorts/sorts_field.js +1 -2
  82. package/build/src/utils/helpers.js +2 -14
  83. package/build/src/validation/schema_builder.d.ts +3 -0
  84. package/build/src/validation/schema_builder.js +38 -0
  85. package/build/src/validation/vine_resolver.js +13 -0
  86. package/build/stubs/main.d.ts +1 -4
  87. package/build/stubs/main.js +1 -2
  88. package/build/types.d.ts +44 -19
  89. package/package.json +26 -24
@@ -0,0 +1,38 @@
1
+ import { resolveVine } from "./vine_resolver.js";
2
+ import app from "@adonisjs/core/services/app";
3
+ import { RuntimeException } from "@adonisjs/core/exceptions";
4
+ // src/validation/schema_builder.ts
5
+ const isVineValidator = (rules) => {
6
+ return typeof rules?.validate === "function";
7
+ };
8
+ const isVineObject = (rules) => {
9
+ return typeof rules.getProperties === "function";
10
+ };
11
+ /**
12
+ * Filters are omitted from the URL most of the time, so every filter rule is made
13
+ * optional while keeping its type and constraints when the value is present.
14
+ */
15
+ const toOptional = (rule) => {
16
+ const schema = rule;
17
+ return typeof schema.optional === "function" ? schema.optional() : rule;
18
+ };
19
+ const buildQueryValidator = async (rules, filterRules = {}) => {
20
+ if (isVineValidator(rules)) return rules;
21
+ const vine = await resolveVine();
22
+ const filterKey = app.config.get("apiquery.parameters.filter", "filter");
23
+ const filterProperties = Object.fromEntries(Object.entries(filterRules).map(([name, rule]) => [name, toOptional(rule)]));
24
+ const hasFilterRules = Object.keys(filterProperties).length > 0;
25
+ if (rules && isVineObject(rules)) {
26
+ const properties = rules.getProperties();
27
+ if (!hasFilterRules || Object.hasOwn(properties, filterKey)) return vine.create(rules);
28
+ return vine.create({
29
+ ...properties,
30
+ [filterKey]: vine.object(filterProperties).optional()
31
+ });
32
+ }
33
+ const properties = { ...rules };
34
+ if (hasFilterRules && !Object.hasOwn(properties, filterKey)) properties[filterKey] = vine.object(filterProperties).optional();
35
+ if (Object.keys(properties).length === 0) throw new RuntimeException("No validation rules defined for the API query request");
36
+ return vine.create(properties);
37
+ };
38
+ export { buildQueryValidator };
@@ -0,0 +1,13 @@
1
+ import { VineNotInstalledException } from "../exceptions/vine_not_installed.js";
2
+ // src/validation/vine_resolver.ts
3
+ let cachedVine;
4
+ const resolveVine = async () => {
5
+ if (cachedVine) return cachedVine;
6
+ try {
7
+ cachedVine = (await import("@vinejs/vine")).default;
8
+ return cachedVine;
9
+ } catch {
10
+ throw new VineNotInstalledException();
11
+ }
12
+ };
13
+ export { resolveVine };
@@ -1,4 +1 @@
1
- //#region stubs/main.d.ts
2
- declare const stubsRoot: string;
3
- //#endregion
4
- export { stubsRoot };
1
+ export declare const stubsRoot: string;
@@ -1,4 +1,3 @@
1
- //#region stubs/main.ts
1
+ // stubs/main.ts
2
2
  const stubsRoot = import.meta.dirname;
3
- //#endregion
4
3
  export { stubsRoot };
package/build/types.d.ts CHANGED
@@ -1,15 +1,28 @@
1
- import { LucidModel, ModelAttributes, ModelQueryBuilderContract } from "@adonisjs/lucid/types/model";
1
+ import { AllowedInclude } from "./src/allowed_include.js";
2
+ import { AllowedSort } from "./src/allowed_sort.js";
3
+ import { ApiQueryBuilderRequest } from "./src/api_query_builder_request.js";
4
+ import { BaseFilterable } from "./src/base_filterable.js";
5
+ import { AllowedFilter } from "./src/allowed_filter.js";
6
+ import { HttpRequest } from "@adonisjs/core/http";
7
+ import { ExtractScopes, LucidModel, ModelAttributes, ModelQueryBuilderContract } from "@adonisjs/lucid/types/model";
2
8
  import { StrictValuesWithoutRaw } from "@adonisjs/lucid/types/querybuilder";
3
-
4
- //#region src/types.d.ts
5
- interface ApiQueryConfigParameters {
9
+ import { SchemaTypes } from "@vinejs/vine/types";
10
+ import { ExtractModelRelations } from "@adonisjs/lucid/types/relations";
11
+ import { Vine, VineObject } from "@vinejs/vine";
12
+ export type QueryValidator = ReturnType<Vine['create']>;
13
+ /**
14
+ * Validation rules accepted for an API query request: a compiled `vine.create(...)`,
15
+ * a `vine.object(...)` schema, or a literal object of Vine schemas.
16
+ */
17
+ export type ValidationRules = QueryValidator | VineObject<Record<string, SchemaTypes>, unknown, unknown, unknown> | Record<string, SchemaTypes>;
18
+ export interface ApiQueryConfigParameters {
6
19
  include?: string;
7
20
  filter?: string;
8
21
  sort?: string;
9
22
  fields?: string;
10
23
  append?: string;
11
24
  }
12
- interface ApiQueryConfig {
25
+ export interface ApiQueryConfig {
13
26
  /**
14
27
  * By default the package will use the `include`, `filter`, `sort`
15
28
  * and `fields` query parameters as described in the readme.
@@ -69,23 +82,35 @@ interface ApiQueryConfig {
69
82
  * Set this to `true` if you want to convert the firstName into first_name for the underlying query
70
83
  */
71
84
  convertFieldNamesToSnakeCase?: boolean;
85
+ /**
86
+ * By default, the package will automatically validate the request before query execution
87
+ * when validation rules are configured.
88
+ *
89
+ * Set this to `false` if you prefer manual validation via `validate()`.
90
+ */
91
+ autoValidate?: boolean;
72
92
  }
73
- type ResolvedApiQueryConfig = Required<ApiQueryConfig> & {
93
+ export type ResolvedApiQueryConfig = Required<ApiQueryConfig> & {
74
94
  parameters: Required<ApiQueryConfigParameters>;
75
95
  };
76
- type EnumType<T> = T[keyof T];
77
- interface Sort<Model extends LucidModel> {
78
- handle: (query: ModelQueryBuilderContract<Model, InstanceType<Model>>, isDescending: boolean, property: string) => void;
96
+ export type EnumType<T> = T[keyof T];
97
+ export interface Sort<Model extends LucidModel> {
98
+ handle(query: ModelQueryBuilderContract<Model, InstanceType<Model>>, isDescending: boolean, property: string): void;
79
99
  }
80
- interface Filter<Model extends LucidModel> {
81
- handle: (query: ModelQueryBuilderContract<Model, InstanceType<Model>>, value: StrictValuesWithoutRaw | null, property: string) => void;
100
+ export interface Filter<Model extends LucidModel> {
101
+ handle(query: ModelQueryBuilderContract<Model, InstanceType<Model>>, value: StrictValuesWithoutRaw | null, property: string): void;
82
102
  }
83
- interface Include<ParentModel extends LucidModel> {
84
- handle: (query: ModelQueryBuilderContract<ParentModel, InstanceType<ParentModel>>, include: string) => void;
103
+ export interface Include<ParentModel extends LucidModel> {
104
+ handle(query: ModelQueryBuilderContract<ParentModel, InstanceType<ParentModel>>, include: string): void;
85
105
  }
86
- type ExtractKeys<T> = T extends object ? { [K in keyof T & string]: K }[keyof T & string] : never;
87
- type ExtractKeysWithSort<T> = T extends object ? { [K in keyof T & string]: `-${K}` }[keyof T & string] : never;
88
- type SortUnionKeyParams<Model extends LucidModel> = ExtractKeys<ModelAttributes<InstanceType<Model>>> | ExtractKeysWithSort<ModelAttributes<InstanceType<Model>>>;
89
- type HintedString<KnownValues> = KnownValues extends string ? (string & {}) | KnownValues : never;
90
- //#endregion
91
- export { ApiQueryConfig, ApiQueryConfigParameters, EnumType, ExtractKeys, ExtractKeysWithSort, Filter, HintedString, Include, ResolvedApiQueryConfig, Sort, SortUnionKeyParams };
106
+ export type ExtractKeys<T> = T extends object ? { [K in keyof T & string]: K; }[keyof T & string] : never;
107
+ export type ExtractKeysWithSort<T> = T extends object ? { [K in keyof T & string]: `-${K}`; }[keyof T & string] : never;
108
+ export type SortUnionKeyParams<Model extends LucidModel> = ExtractKeys<ModelAttributes<InstanceType<Model>>> | ExtractKeysWithSort<ModelAttributes<InstanceType<Model>>>;
109
+ export type ScopeUnionKeyParams<Model extends LucidModel> = [keyof ExtractScopes<Model>] extends [never] ? string : HintedString<keyof ExtractScopes<Model>>;
110
+ export type HintedString<KnownValues> = KnownValues extends string ? (string & {}) | KnownValues : never;
111
+ export type FilterableConstructor<Model extends LucidModel = LucidModel> = new (request?: HttpRequest | ApiQueryBuilderRequest) => BaseFilterable<Model>;
112
+ export type FilterableThunk<Model extends LucidModel = LucidModel> = () => FilterableConstructor<Model>;
113
+ export type FilterableTarget<Model extends LucidModel = LucidModel> = FilterableConstructor<Model> | FilterableThunk<Model>;
114
+ export type FilterItem<Model extends LucidModel = LucidModel> = AllowedFilter<Model> | HintedString<ExtractKeys<ModelAttributes<InstanceType<Model>>>> | (string & {});
115
+ export type SortItem<Model extends LucidModel = LucidModel> = AllowedSort<Model> | HintedString<SortUnionKeyParams<Model>> | (string & {});
116
+ export type IncludeItem<Model extends LucidModel = LucidModel> = AllowedInclude<Model> | AllowedInclude<Model>[] | HintedString<ExtractModelRelations<InstanceType<Model>>> | (string & {});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eienjs/adonisjs-api-query",
3
3
  "type": "module",
4
- "version": "2.0.0",
4
+ "version": "3.0.0",
5
5
  "description": "Easily build Lucid queries from API requests",
6
6
  "author": "Fernando Isidro <luffynando@gmail.com>",
7
7
  "license": "MIT",
@@ -40,45 +40,47 @@
40
40
  "peerDependencies": {
41
41
  "@adonisjs/assembler": "^8.4.0",
42
42
  "@adonisjs/core": "^7.3.4",
43
- "@adonisjs/lucid": "^22.4.2"
43
+ "@adonisjs/lucid": "^22.4.2",
44
+ "@vinejs/vine": ">=4.4.0"
44
45
  },
45
46
  "peerDependenciesMeta": {
46
47
  "@adonisjs/assembler": {
47
48
  "optional": true
49
+ },
50
+ "@vinejs/vine": {
51
+ "optional": true
48
52
  }
49
53
  },
50
- "dependencies": {
51
- "collect.js": "^4.36.1"
52
- },
54
+ "dependencies": {},
53
55
  "devDependencies": {
54
- "@adonisjs/assembler": "^8.4.0",
55
- "@adonisjs/core": "^7.3.4",
56
+ "@adonisjs/assembler": "^8.6.0",
57
+ "@adonisjs/core": "^7.6.0",
56
58
  "@adonisjs/eslint-plugin": "^2.2.2",
57
59
  "@adonisjs/lucid": "^22.4.2",
58
60
  "@adonisjs/tsconfig": "^2.0.0",
59
- "@codenameryuu/adonis-lucid-soft-deletes": "^2.0.3",
60
- "@commitlint/cli": "^21.1.0",
61
- "@commitlint/config-conventional": "^21.1.0",
62
- "@eienjs/eslint-config": "^2.1.2",
61
+ "@commitlint/cli": "^21.2.4",
62
+ "@commitlint/config-conventional": "^21.2.4",
63
+ "@eienjs/adonisjs-soft-deletes": "^1.0.1",
64
+ "@eienjs/eslint-config": "^2.3.1",
63
65
  "@japa/assert": "^4.2.0",
64
66
  "@japa/file-system": "^3.0.0",
65
- "@japa/runner": "^5.3.0",
66
- "@poppinss/hooks": "^7.3.0",
67
- "@poppinss/ts-exec": "^1.4.4",
68
- "@types/luxon": "^3.7.2",
69
- "@types/node": "^24.13.2",
70
- "auto-changelog": "^2.6.0",
71
- "better-sqlite3": "^12.11.1",
72
- "c8": "^11.0.0",
67
+ "@japa/runner": "^5.4.0",
68
+ "@poppinss/hooks": "^7.3.1",
69
+ "@poppinss/ts-exec": "^1.5.1",
70
+ "@types/luxon": "^3.7.6",
71
+ "@types/node": "^26.6.6",
72
+ "@vinejs/vine": "^4.4.0",
73
+ "auto-changelog": "^2.7.0",
74
+ "better-sqlite3": "^13.0.3",
75
+ "c8": "^12.0.0",
73
76
  "enforce-unique": "^1.3.0",
74
- "eslint": "^10.6.0",
75
- "eslint-plugin-erasable-syntax-only": "^0.4.2",
77
+ "eslint": "^10.13.0",
78
+ "eslint-plugin-erasable-syntax-only": "^0.7.2",
76
79
  "husky": "^9.1.7",
77
80
  "is-in-ci": "^2.0.0",
78
81
  "luxon": "^3.7.2",
79
- "release-it": "^20.2.1",
80
- "tsdown": "^0.22.3",
81
- "tsdown-stale-guard": "^0.1.2",
82
+ "release-it": "^21.1.1",
83
+ "tsdown": "^0.23.3",
82
84
  "typescript": "^6.0.3"
83
85
  },
84
86
  "c8": {