@dav3/gql-of-power 1.3.0 → 1.4.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 (48) hide show
  1. package/.fallowrc.json +23 -0
  2. package/.prettierignore +13 -0
  3. package/README.md +24 -0
  4. package/agents/templates/entity-skeleton.ts.txt +136 -0
  5. package/dist/entities/gql-entity.d.ts +2 -1
  6. package/dist/entities/gql-entity.js +303 -281
  7. package/dist/field-operations.d.ts +102 -0
  8. package/dist/field-operations.js +95 -0
  9. package/dist/operations.d.ts +5 -102
  10. package/dist/operations.js +17 -82
  11. package/dist/queries/filter-processor.d.ts +5 -2
  12. package/dist/queries/filter-processor.js +58 -53
  13. package/dist/queries/gql-to-sql-mapper.d.ts +8 -1
  14. package/dist/queries/gql-to-sql-mapper.js +193 -172
  15. package/dist/queries/relationship-handler.d.ts +3 -1
  16. package/dist/queries/relationship-handler.js +7 -6
  17. package/dist/queries/sql-builder.d.ts +3 -52
  18. package/dist/queries/sql-builder.js +4 -73
  19. package/dist/queries/utils.d.ts +1 -2
  20. package/dist/queries/utils.js +1 -8
  21. package/dist/query-manager.d.ts +14 -2
  22. package/dist/query-manager.js +60 -54
  23. package/dist/types/gql-to-sql-types.d.ts +1 -1
  24. package/dist/types/gql-to-sql-types.js +2 -2
  25. package/dist/types/gql-types.d.ts +2 -3
  26. package/dist/types/sql-types.d.ts +7 -2
  27. package/dist/types/sql-types.js +2 -0
  28. package/dist/variables.d.ts +0 -1
  29. package/dist/variables.js +4 -5
  30. package/fallow-baselines/dead-code.json +34 -0
  31. package/fallow-baselines/dupes.json +10 -0
  32. package/fallow-baselines/health.json +103 -0
  33. package/package.json +14 -11
  34. package/src/entities/gql-entity.ts +452 -294
  35. package/src/field-operations.ts +134 -0
  36. package/src/operations.ts +6 -120
  37. package/src/queries/filter-processor.ts +54 -24
  38. package/src/queries/gql-to-sql-mapper.ts +343 -240
  39. package/src/queries/relationship-handler.ts +4 -9
  40. package/src/queries/sql-builder.ts +5 -102
  41. package/src/queries/utils.ts +2 -11
  42. package/src/query-manager.ts +90 -65
  43. package/src/types/access-control.ts +1 -0
  44. package/src/types/gql-to-sql-types.ts +2 -2
  45. package/src/types/gql-types.ts +11 -9
  46. package/src/types/sql-types.ts +11 -5
  47. package/src/types/utils.ts +3 -3
  48. package/src/variables.ts +1 -1
package/.fallowrc.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "./node_modules/fallow/schema.json",
3
+ "entry": [
4
+ "src/index.{ts,tsx,js,jsx}",
5
+ "scripts/update-version.ts",
6
+ "tests/run-sample-tests.ts",
7
+ "tests/regression/generate-sql-snapshots.ts"
8
+ ],
9
+ "ignorePatterns": ["dist/**", "coverage/**", "tests/fixtures/**", "examples/**"],
10
+ "ignoreDependencies": [],
11
+ "ignoreExports": [
12
+ {
13
+ "file": "src/types/gql-types.ts",
14
+ "exports": ["*"],
15
+ "reason": "Generated filter/pagination input type shapes consumed via type-graphql decorators, not direct imports."
16
+ }
17
+ ],
18
+ "audit": {
19
+ "deadCodeBaseline": "fallow-baselines/dead-code.json",
20
+ "healthBaseline": "fallow-baselines/health.json",
21
+ "dupesBaseline": "fallow-baselines/dupes.json"
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ # Build output
2
+ dist/
3
+ coverage/
4
+
5
+ # Lockfiles (binary / auto-generated)
6
+ bun.lockb
7
+ pnpm-lock.yaml
8
+ package-lock.json
9
+ yarn.lock
10
+
11
+ # fallow caches (auto-managed) + machine-generated baselines
12
+ .fallow/
13
+ fallow-baselines/
package/README.md CHANGED
@@ -568,6 +568,27 @@ No `limit` or `offset` parameters are accepted — `LIMIT 1` is always applied.
568
568
 
569
569
  ---
570
570
 
571
+ ## Agent Integration
572
+
573
+ This library ships an **agent skill** (`agents/`) that teaches AI coding assistants (Claude Code, Cursor, Hermes, or any agent reading `AGENTS.md`) how to scaffold gql-of-power entities, relationships, and advanced features correctly — using copy-paste templates, annotated relationship examples, and a decision tree for picking the right relationship type.
574
+
575
+ To install it in a consumer project (one-time setup):
576
+
577
+ ```bash
578
+ # Claude Code
579
+ mkdir -p .claude/skills && cp -R node_modules/@dav3/gql-of-power/agents/ .claude/skills/gql-of-power/
580
+
581
+ # Cursor
582
+ mkdir -p .cursor/rules && cp node_modules/@dav3/gql-of-power/agents/SKILL.md .cursor/rules/gql-of-power.mdc
583
+
584
+ # Hermes
585
+ ln -s node_modules/@dav3/gql-of-power/agents ~/.hermes/skills/gql-of-power
586
+ ```
587
+
588
+ See [`agents/install.md`](./agents/install.md) for full instructions (including a symlink option that tracks gql-of-power updates, slash-command setup, and a generic `AGENTS.md` snippet). Once installed, ask your agent to "add a gql-of-power entity" and it will follow the library's intended patterns.
589
+
590
+ ---
591
+
571
592
  ## Development
572
593
 
573
594
  ```bash
@@ -575,8 +596,11 @@ bun install # Install dependencies
575
596
  bun run build # Compile TypeScript → dist/
576
597
  bun run test # Run all tests
577
598
  bun run test:watch # Watch mode
599
+ bun run audit # Codebase-intelligence gate (fallow): dead code, duplication, complexity
578
600
  ```
579
601
 
602
+ `bun run audit` runs the changed-file gate against saved baselines in `.fallow/` and fails only on **new** findings it introduces. Use `bun run audit:full` for the complete report, or `bun run audit:dead-code` / `audit:dupes` / `audit:health` for individual analyses. See `AGENTS.md` for the full audit workflow.
603
+
580
604
  ---
581
605
 
582
606
  ## License
@@ -0,0 +1,136 @@
1
+ // gql-of-power entity skeleton — copy, rename, and adapt.
2
+ // Middle-earth themed example: a "Weapon" entity with a m:1 to its Bearer.
3
+ //
4
+ // This file is a template, not importable code. Place your real files in:
5
+ // schema/entities.ts (ORM class + EntityMetadata)
6
+ // schema/fields.ts (FieldsSettings)
7
+ // graphql/entities.ts (createGQLTypes calls)
8
+ // graphql/resolvers.ts (@Resolver + @Query classes)
9
+
10
+ import 'reflect-metadata';
11
+ import { GraphQLResolveInfo } from 'graphql';
12
+ import {
13
+ createGQLTypes,
14
+ GQLEntityFilterInputFieldType,
15
+ FieldsSettings,
16
+ } from '@dav3/gql-of-power';
17
+ import { EntityMetadata, EntityProperty, ReferenceType } from '@dav3/gql-of-power';
18
+ import { Arg, Info, Int, Query, Resolver } from 'type-graphql';
19
+ import { queryManager, provider } from './your-app-setup'; // <-- your wiring
20
+
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+ // 1. ORM ENTITY CLASS (schema/entities.ts)
23
+ // Plain TS class. Property names = JS names. Relationship fields optional.
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+
26
+ export class Weapon {
27
+ id!: number;
28
+ name!: string;
29
+ damage!: number;
30
+ // m:1 relationship — Weapon holds bearerId
31
+ bearerId?: number;
32
+ bearer?: Person;
33
+ }
34
+
35
+ export class Person {
36
+ id!: number;
37
+ name!: string;
38
+ }
39
+
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ // 2. ENTITY METADATA (schema/entities.ts)
42
+ // SQL reality: table, PKs, column names, relationship join columns.
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+
45
+ const prop = (
46
+ type: string,
47
+ name: string,
48
+ fieldNames: string[],
49
+ reference?: {
50
+ referenceType: ReferenceType;
51
+ joinColumns?: string[];
52
+ referencedColumnNames?: string[];
53
+ mappedBy?: string;
54
+ }
55
+ ): EntityProperty => ({
56
+ type,
57
+ name,
58
+ fieldNames,
59
+ mappedBy: reference?.mappedBy || '',
60
+ joinColumns: reference?.joinColumns || [],
61
+ referencedColumnNames: reference?.referencedColumnNames || [],
62
+ inverseJoinColumns: [],
63
+ pivotTable: '',
64
+ reference: reference?.referenceType,
65
+ });
66
+
67
+ export const WeaponMetadata: EntityMetadata<Weapon> = {
68
+ name: 'Weapon',
69
+ tableName: 'weapons',
70
+ primaryKeys: ['id'],
71
+ properties: {
72
+ id: prop('number', 'id', ['id']),
73
+ name: prop('string', 'name', ['weapon_name']),
74
+ damage: prop('number', 'damage', ['damage']),
75
+ bearerId: prop('number', 'bearerId', ['bearer_id']),
76
+ bearer: prop('Person', 'bearer', ['bearer_id'], {
77
+ referenceType: ReferenceType.MANY_TO_ONE,
78
+ joinColumns: ['bearer_id'],
79
+ referencedColumnNames: ['id'],
80
+ }),
81
+ },
82
+ };
83
+
84
+ export const PersonMetadata: EntityMetadata<Person> = {
85
+ name: 'Person',
86
+ tableName: 'persons',
87
+ primaryKeys: ['id'],
88
+ properties: {
89
+ id: prop('number', 'id', ['id']),
90
+ name: prop('string', 'name', ['person_name']),
91
+ },
92
+ };
93
+
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+ // 3. FIELDS SETTINGS (schema/fields.ts)
96
+ // How each field maps to GraphQL. Pass to createGQLTypes.
97
+ // ─────────────────────────────────────────────────────────────────────────────
98
+
99
+ // Create PersonGQL first (Weapon references it)
100
+ export const PersonGQL = createGQLTypes(Person, {
101
+ id: { type: () => Number, generateFilter: true },
102
+ name: { type: () => String, generateFilter: true },
103
+ });
104
+
105
+ export const WeaponFields: Partial<FieldsSettings<Weapon>> = {
106
+ id: { type: () => Number, generateFilter: true },
107
+ name: { type: () => String, generateFilter: true },
108
+ damage: { type: () => Int, generateFilter: true },
109
+ bearer: {
110
+ type: () => PersonGQL.GQLEntity,
111
+ options: { nullable: true },
112
+ generateFilter: true,
113
+ relatedEntityName: () => Person.name,
114
+ getFilterType: () => Int,
115
+ },
116
+ };
117
+
118
+ // ─────────────────────────────────────────────────────────────────────────────
119
+ // 4. WIRE + RESOLVE (graphql/entities.ts, graphql/resolvers.ts)
120
+ // ─────────────────────────────────────────────────────────────────────────────
121
+
122
+ export const WeaponGQL = createGQLTypes(Weapon, WeaponFields);
123
+
124
+ @Resolver(() => WeaponGQL.GQLEntity || Object)
125
+ export class WeaponResolver extends WeaponGQL.FieldsResolver {
126
+ @Query(() => [WeaponGQL.GQLEntity])
127
+ async weapons(
128
+ @Info() info: GraphQLResolveInfo,
129
+ @Arg('filter', () => WeaponGQL.GQLEntityFilterInput || Object, { nullable: true })
130
+ filter?: GQLEntityFilterInputFieldType<Weapon>
131
+ ) {
132
+ // Relationship-field filters need `as any` (the type doesn't surface them):
133
+ // { bearer: { name: 'Aragorn' } } as any
134
+ return await queryManager.getQueryResultsForInfo(provider, Weapon, info, filter);
135
+ }
136
+ }
@@ -1,5 +1,6 @@
1
1
  import { getMetadataStorage } from 'type-graphql';
2
- import { CountFieldMeta, CustomFieldsSettings, FieldSettings, FieldsSettings, GQLEntityFilterInputFieldType, GQLEntityPaginationInputType, RelatedFieldSettings } from '../types';
2
+ import { CountFieldMeta, CustomFieldsSettings, FieldSettings, FieldsSettings, RelatedFieldSettings } from '../types/sql-types';
3
+ import { GQLEntityFilterInputFieldType, GQLEntityPaginationInputType } from '../types/gql-types';
3
4
  import { AccessControlEntry } from '../types/access-control';
4
5
  export declare const setGlobalConfig: (config: {
5
6
  gqlTypesSuffix?: string;