@nxara/olio-codegen 0.1.1 → 0.1.2

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 (2) hide show
  1. package/README.md +5 -232
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,239 +1,12 @@
1
1
  # @nxara/olio-codegen
2
2
 
3
- Generate TypeScript types from GraphQL SDL.
4
-
5
- `@nxara/olio-codegen` discovers modules from a directory layout, reads each module's GraphQL SDL, and emits a single strongly typed TypeScript file: schema types, per-module operation contracts, and the application type.
6
-
7
- It is deliberately small - SDL in, types out. Schema composition belongs to `@nxara/olio-schema`, the runtime to `@nxara/olio`.
8
-
9
- ## Install
3
+ Generate TypeScript types from GraphQL SDL: the schema's types and the `Application` contract that types olio's builders, plus typed operations for `@nxara/olio-client`.
10
4
 
11
5
  ```sh
12
- bun add @nxara/olio-codegen graphql@16
13
- ```
14
-
15
- `graphql` 16 is a peer dependency.
16
-
17
- ## Usage
18
-
19
- There is no bundled CLI; drive codegen from a short script:
20
-
21
- ```ts
22
- import { codegen } from "@nxara/olio-codegen";
23
-
24
- await codegen({
25
- root: import.meta.dirname,
26
- modules: "src/modules",
27
- typeDefs: "**/typedefs/**/*.gql",
28
- output: "src/generated.ts"
29
- });
30
- ```
31
-
32
- Relative paths (`modules`, `output`, `schemaOutput`) resolve against `root`, which defaults to the working directory; pass `import.meta.dirname` so the script runs the same from any folder. `typeDefs` is the exception: it resolves inside each module. `codegenClient` takes `root` the same way, for `schema` (unless it's a URL), `documents` and `output`.
33
-
34
- Both reject with a message that names the file and position. Uncaught, the runtime prints it with a stack; to print just the message:
35
-
36
- ```ts
37
- await codegen({ ... }).catch((error: Error) => {
38
- console.error(error.message);
39
- process.exitCode = 1;
40
- });
41
- ```
42
-
43
- `modules` points to a directory whose **direct child directories** are modules. `typeDefs` is resolved relative to each module directory. `codegen` discovers modules, loads and parses their SDL, generates TypeScript, and writes the output (creating parent directories as needed).
44
-
45
- SDL files are read exactly as `loadTypeDefs` in `@nxara/olio-tools` reads them (both go through `@nxara/olio-core/files`): `.graphql` / `.gql`, or `.ts` / `.js` files whose default export is SDL or a `DocumentNode`, so an app moving from untyped to typed keeps its SDL where it is (`typeDefs: "**/typedefs/**/*.{gql,ts}"`). Codegen imports `.ts` files, so run it with something that executes TypeScript (bun, tsx, or Node 23.6+).
46
-
47
- ### Directives
48
-
49
- Pass `directives` to apply `@nxara/olio-schema` transformations before generation:
50
-
51
- ```ts
52
- import { codegen } from "@nxara/olio-codegen";
53
- import { copyDirective, implementsDirective } from "@nxara/olio-schema";
54
-
55
- await codegen({
56
- modules: "src/modules",
57
- typeDefs: "**/typedefs/**/*.gql",
58
- output: "src/generated.ts",
59
- schemaOutput: "src/schema.graphql",
60
- directives: [implementsDirective(), copyDirective()]
61
- });
62
- ```
63
-
64
- Their names land in `Application` as `"~directives"`, and olio's types then require the same `directives` in `olio()`.
65
-
66
- Set `schemaOutput` to write the final schema to a file, printed from the built schema: each type once with its extensions merged in, the directives applied, ready for schema registries, linters and diff tools. Omit it to generate only TypeScript. The schema is validated before anything is written, so an invalid one fails codegen rather than the server.
67
-
68
- ## Client types
69
-
70
- `codegenClient({ schema, documents, output })` generates the types for [`@nxara/olio-client`](../client), the way graphql-codegen does: `schema` is an SDL file (typically the `schemaOutput` file above) or the running server's URL, introspected (`headers` for one that needs a token); `documents` are the client's `.graphql` operations. The operations are checked against the schema first, with `file:line:column` errors. `watch: true` reruns when an operation (including a new file) or the schema file changes; a server URL isn't watched.
71
-
72
- Subscriptions go over WebSocket unless marked `@sse` (`subscription Feed @sse { … }`); `@ws` says the default explicitly. The directive is the client's: codegen records it in the document and removes it from the query the server receives, and imports only the transports the operations use. `@ws` / `@sse` on a query or mutation, both on one subscription, or with arguments, is an error.
73
-
74
- Per operation it emits the result type, the variables type and a typed document (`LoginMutation`, `LoginMutationVariables`, `LoginDocument`), plus `operations` for `createClient`. Types are what a client sees on the wire: enums by name, scalars through `ClientScalars`.
75
-
76
- ```ts
77
- import { codegenClient } from "@nxara/olio-codegen";
78
-
79
- await codegenClient({
80
- root: import.meta.dirname,
81
- schema: "../server/src/schema.gql", // or "http://localhost:4000/graphql"
82
- documents: "src/operations/*.graphql",
83
- output: "src/generated.ts"
84
- });
85
- ```
86
-
87
- ## Modules
88
-
89
- Every direct child directory of `modules` is one module. The directory name is the module ID, and there are no nested modules:
90
-
91
- ```
92
- src/modules/
93
- ├── auth/
94
- │ ├── typedefs/auth.gql
95
- │ └── query.ts
96
- ├── user/
97
- │ └── typedefs/user.gql
98
- └── sales/
99
- ├── typedefs/sales.gql
100
- └── orders/
101
- └── typedefs/orders.gql
102
- ```
103
-
104
- This yields modules `auth`, `user`, and `sales`. `typeDefs` resolves inside each module (`src/modules/auth/**/typedefs/**/*.gql`, and so on), so each module's files belong exclusively to that module.
105
-
106
- A direct subfolder of a module in which `typeDefs` finds files of its own is a **feature** of that module: here `sales/orders`, with its own entry in `Application` (`"sales/orders"`). Features don't nest.
107
-
108
- So the glob decides what is a feature, because codegen runs it inside each subfolder. `**/typedefs/**/*.gql` finds nothing inside `users/typedefs/` itself (there's no `typedefs/` in it), so that folder stays the module's own SDL. `**/*.gql` does find its files, so it makes `typedefs` a feature of every module (`"users/typedefs"`), and `users` ends up with no fields. Keep SDL in `typedefs/` folders and use `**/typedefs/**/*.gql`. The ID is used as-is as the module's key in `Application` (quoted when it is not a valid identifier).
109
-
110
- ## Application
111
-
112
- `Application` lists, per module, the types it declares fields on. Register it with `@nxara/olio`, which derives resolver signatures and middleware targets from it:
113
-
114
- ```ts
115
- export type Application = {
116
- users: {
117
- Query: {
118
- type: Query;
119
- fields: "user" | "users";
120
- args: {
121
- user: { id: string };
122
- };
123
- };
124
- User: { type: User; fields: "__isTypeOf" | "id" | "name" };
125
- };
126
- posts: {
127
- Post: { type: Post; fields: "__isTypeOf" | "id" };
128
- User: { type: User; fields: "posts" }; // from `extend type User { posts: [Post!]! }`
129
- };
130
- };
131
- ```
132
-
133
- - **`type`** is the parent type. Resolvers receive it as `root` and return `type[field]`.
134
- - **`fields`** are the fields the module owns. A module owns every field it declares, whether in a definition or an `extend`, on root and non-root types alike. The defining module also owns `__isTypeOf` (objects) or `__resolveType` (interfaces and unions, which also list their possible types as `resolveType`).
135
- - **`args`** appears only for fields that take arguments.
136
- - **Enums** appear as `{ type; fields: never; enum: "A" | "B" }`, listing the values the module declares (an `extend enum` belongs to the extending module).
137
- - **Scalars** appear as `{ type; fields: never; scalar: true }` under the module that defines them, which supplies the implementation.
138
- - **`bypass`** lists, per owned field, the keys its `@bypass` gives it (`bypass: { login: "audit" | "auth" }`). olio types middleware's `bypass` set from every key listed here.
139
- - **`root`** marks a root type with its operation (`root: "query"`), so olio finds the roots whatever they're named. A hand-written `Application` needs it too.
140
-
141
- Middleware targets aren't emitted: a module's targets are its owned fields, and the application's are the root fields.
142
-
143
- ## Supported SDL
144
-
145
- - Object types, interfaces, unions, enums, inputs, and scalars
146
- - Lists and nullability (`String`, `[String!]!`, …), mapped exactly
147
- - Type extensions (`extend type User { … }`)
148
- - Root operation types, under the default names or your own (`schema { query: RootQuery }`)
149
-
150
- A schema type can't be named `Application`, `Maybe`, `ResolveEnum`, `ResolveScalar` or `Scalars`, since the generated file declares those names itself.
151
-
152
- ## Custom scalars
153
-
154
- Every scalar goes through one exported table, `Scalars`, so fields read `Scalars["DateTime"]["output"]`. Built-in scalars default to their usual TypeScript types (`ID`/`String` → `string`, `Int`/`Float` → `number`, `Boolean` → `boolean`), and custom scalars default to `unknown`. Register or remap any of them by augmenting `CustomScalars`:
155
-
156
- ```ts
157
- declare module "@nxara/olio-codegen" {
158
- interface CustomScalars {
159
- DateTime: Date;
160
- JSON: { input: string; output: unknown };
161
- ID: string | ObjectId; // built-ins can be remapped too
162
- }
163
- }
164
- ```
165
-
166
- Use the table directly when you need a scalar's type: `Scalars["DateTime"]["input"]`.
167
-
168
- Like every registration, it goes in a `.ts` file, or a `.d.ts` with an `import` or `export {}`.
169
-
170
- ## Enums with internal values
171
-
172
- Enums are their GraphQL names by default (`"ADMIN" | "GUEST"`). To store something else, such as a number or a lowercase string, register the internal type by augmenting `CustomEnums`:
173
-
174
- ```ts
175
- declare module "@nxara/olio-codegen" {
176
- interface CustomEnums {
177
- OrderStatus: "confirmed" | "cancelled";
178
- }
179
- }
180
- ```
181
-
182
- Every generated position then uses it: object fields, arguments, and inputs, since graphql converts enum inputs to internal values too. Supply the values at runtime through the owning module's `enums` option. Once `CustomEnums` registers an enum, olio's types require it there, with a value for every value the module declares:
183
-
184
- ```ts
185
- export const sales = module.sales({
186
- typeDefs,
187
- resolvers,
188
- enums: { OrderStatus: { CONFIRMED: "confirmed", CANCELLED: "cancelled" } }
189
- });
190
- ```
191
-
192
- To keep a map in its own file, write it with the typed builder: `export const OrderStatus = resolvers.sales.OrderStatus({ ... })`, then `enums: { OrderStatus }`, or load such files with `enums: await loadEnums(...)` from `@nxara/olio-tools` (unchecked, like `loadResolvers`).
193
-
194
- ## Output
195
-
196
- `codegen` writes a single TypeScript file to `output`. For:
197
-
198
- ```graphql
199
- type Query {
200
- user: User
201
- }
202
- type User {
203
- id: ID!
204
- name: String
205
- }
206
- ```
207
-
208
- the emitted `User` type is:
209
-
210
- ```ts
211
- export type User = {
212
- __typename?: "User";
213
- id: Scalars["ID"]["output"];
214
- name: Maybe<Scalars["String"]["output"]>;
215
- };
6
+ bun add -d @nxara/olio-codegen
7
+ bun add graphql@16
216
8
  ```
217
9
 
218
- The full output contains:
219
-
220
- - the `Scalars` table
221
- - enums as `const` objects plus union types
222
- - object types (root types included), interfaces, inputs, and unions. Nullable inputs without a default are optional keys.
223
- - the `Application` type
224
-
225
- The header marks the file as generated:
226
-
227
- ```ts
228
- // Generated by @nxara/olio-codegen - do not edit.
229
- ```
230
-
231
- ## Relationship to @nxara/olio-schema
232
-
233
- `@nxara/olio-schema` composes a schema into ordinary GraphQL; `@nxara/olio-codegen` then turns that GraphQL into TypeScript:
234
-
235
- ```
236
- SDL → @nxara/olio-schema → DocumentNode → @nxara/olio-codegen → TypeScript
237
- ```
10
+ **[Read the docs](https://olio.nxara.dev/reference/codegen/)**
238
11
 
239
- Codegen has no knowledge of `@implements` or `@copy` - it accepts `SchemaDirective[]` and passes the parsed document through the transformation pipeline.
12
+ Part of [olio](https://olio.nxara.dev): modular, type-safe GraphQL for TypeScript. MIT licensed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxara/olio-codegen",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Generate TypeScript types from GraphQL SDL.",
5
5
  "keywords": [
6
6
  "codegen",
@@ -34,8 +34,8 @@
34
34
  "check-types": "tsc -p tsconfig.test.json"
35
35
  },
36
36
  "dependencies": {
37
- "@nxara/olio-core": "0.1.1",
38
- "@nxara/olio-schema": "0.1.1"
37
+ "@nxara/olio-core": "0.1.2",
38
+ "@nxara/olio-schema": "0.1.2"
39
39
  },
40
40
  "devDependencies": {
41
41
  "graphql": "^16.14.2"