@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.
- package/.fallowrc.json +23 -0
- package/.prettierignore +13 -0
- package/README.md +24 -0
- package/agents/templates/entity-skeleton.ts.txt +136 -0
- package/dist/entities/gql-entity.d.ts +2 -1
- package/dist/entities/gql-entity.js +303 -281
- package/dist/field-operations.d.ts +102 -0
- package/dist/field-operations.js +95 -0
- package/dist/operations.d.ts +5 -102
- package/dist/operations.js +17 -82
- package/dist/queries/filter-processor.d.ts +5 -2
- package/dist/queries/filter-processor.js +58 -53
- package/dist/queries/gql-to-sql-mapper.d.ts +8 -1
- package/dist/queries/gql-to-sql-mapper.js +193 -172
- package/dist/queries/relationship-handler.d.ts +3 -1
- package/dist/queries/relationship-handler.js +7 -6
- package/dist/queries/sql-builder.d.ts +3 -52
- package/dist/queries/sql-builder.js +4 -73
- package/dist/queries/utils.d.ts +1 -2
- package/dist/queries/utils.js +1 -8
- package/dist/query-manager.d.ts +14 -2
- package/dist/query-manager.js +60 -54
- package/dist/types/gql-to-sql-types.d.ts +1 -1
- package/dist/types/gql-to-sql-types.js +2 -2
- package/dist/types/gql-types.d.ts +2 -3
- package/dist/types/sql-types.d.ts +7 -2
- package/dist/types/sql-types.js +2 -0
- package/dist/variables.d.ts +0 -1
- package/dist/variables.js +4 -5
- package/fallow-baselines/dead-code.json +34 -0
- package/fallow-baselines/dupes.json +10 -0
- package/fallow-baselines/health.json +103 -0
- package/package.json +14 -11
- package/src/entities/gql-entity.ts +452 -294
- package/src/field-operations.ts +134 -0
- package/src/operations.ts +6 -120
- package/src/queries/filter-processor.ts +54 -24
- package/src/queries/gql-to-sql-mapper.ts +343 -240
- package/src/queries/relationship-handler.ts +4 -9
- package/src/queries/sql-builder.ts +5 -102
- package/src/queries/utils.ts +2 -11
- package/src/query-manager.ts +90 -65
- package/src/types/access-control.ts +1 -0
- package/src/types/gql-to-sql-types.ts +2 -2
- package/src/types/gql-types.ts +11 -9
- package/src/types/sql-types.ts +11 -5
- package/src/types/utils.ts +3 -3
- 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
|
+
}
|
package/.prettierignore
ADDED
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,
|
|
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;
|