@soda-gql/core 0.0.9 → 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.
Files changed (38) hide show
  1. package/README.md +312 -0
  2. package/dist/{index-CFNJ_Aa6.d.ts → index-DYwkqPzd.d.ts} +124 -294
  3. package/dist/index-DYwkqPzd.d.ts.map +1 -0
  4. package/dist/index-Db9ogofS.d.ts +365 -0
  5. package/dist/index-Db9ogofS.d.ts.map +1 -0
  6. package/dist/{index-DHh8XRal.d.cts → index-Dth0NSJt.d.cts} +124 -294
  7. package/dist/index-Dth0NSJt.d.cts.map +1 -0
  8. package/dist/index-zGZ61WLt.d.cts +365 -0
  9. package/dist/index-zGZ61WLt.d.cts.map +1 -0
  10. package/dist/index.cjs +132 -44
  11. package/dist/index.d.cts +3 -2
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +129 -45
  14. package/dist/index.js.map +1 -1
  15. package/dist/merge-CeMx09is.js +74 -0
  16. package/dist/merge-CeMx09is.js.map +1 -0
  17. package/dist/merge-ZxKV1syS.cjs +85 -0
  18. package/dist/metadata/index.cjs +62 -0
  19. package/dist/metadata/index.d.cts +71 -0
  20. package/dist/metadata/index.d.cts.map +1 -0
  21. package/dist/metadata/index.d.ts +71 -0
  22. package/dist/metadata/index.d.ts.map +1 -0
  23. package/dist/metadata/index.js +59 -0
  24. package/dist/metadata/index.js.map +1 -0
  25. package/dist/runtime/index.cjs +5 -3
  26. package/dist/runtime/index.d.cts +2 -1
  27. package/dist/runtime/index.d.cts.map +1 -1
  28. package/dist/runtime/index.d.ts +2 -1
  29. package/dist/runtime/index.d.ts.map +1 -1
  30. package/dist/runtime/index.js +5 -3
  31. package/dist/runtime/index.js.map +1 -1
  32. package/dist/{slice-ua5mSfhV.js → slice-BuSNc8vw.js} +27 -5
  33. package/dist/slice-BuSNc8vw.js.map +1 -0
  34. package/dist/{slice-DlVY4UJG.cjs → slice-C-FIQK-f.cjs} +43 -3
  35. package/package.json +8 -1
  36. package/dist/index-CFNJ_Aa6.d.ts.map +0 -1
  37. package/dist/index-DHh8XRal.d.cts.map +0 -1
  38. package/dist/slice-ua5mSfhV.js.map +0 -1
package/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # @soda-gql/core
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@soda-gql/core.svg)](https://www.npmjs.com/package/@soda-gql/core)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Core GraphQL types and utilities for the soda-gql ecosystem. This package provides the foundational building blocks for writing type-safe GraphQL operations.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ bun add @soda-gql/core
12
+ ```
13
+
14
+ ## Core Concepts
15
+
16
+ soda-gql uses three main building blocks for constructing GraphQL operations:
17
+
18
+ ### Models
19
+
20
+ Reusable type-safe fragments with data normalization. Models define how to select fields from a GraphQL type and optionally transform the result.
21
+
22
+ ### Slices
23
+
24
+ Domain-specific query/mutation/subscription pieces. Slices are reusable operation fragments that can be composed into complete operations.
25
+
26
+ ### Operations
27
+
28
+ Complete GraphQL operations that can be executed. There are two types:
29
+ - **Composed Operations**: Built by combining multiple slices
30
+ - **Inline Operations**: Self-contained operations with field selections defined directly
31
+
32
+ ## Usage
33
+
34
+ All soda-gql definitions use the `gql.default()` pattern, which is provided by the generated GraphQL system:
35
+
36
+ ```typescript
37
+ import { gql } from "@/graphql-system";
38
+ ```
39
+
40
+ ### Writing Models
41
+
42
+ Models define reusable fragments for a specific GraphQL type:
43
+
44
+ ```typescript
45
+ export const userModel = gql.default(({ model }, { $var }) =>
46
+ model.User(
47
+ { variables: [$var("includeEmail").scalar("Boolean:?")] },
48
+ ({ f, $ }) => [
49
+ f.id(),
50
+ f.name(),
51
+ f.email({ if: $.includeEmail }),
52
+ ],
53
+ (selection) => ({
54
+ id: selection.id,
55
+ name: selection.name,
56
+ email: selection.email,
57
+ }),
58
+ ),
59
+ );
60
+ ```
61
+
62
+ ### Writing Slices
63
+
64
+ Slices define reusable operation fragments:
65
+
66
+ ```typescript
67
+ export const userSlice = gql.default(({ query }, { $var }) =>
68
+ query.slice(
69
+ { variables: [$var("userId").scalar("ID:!")] },
70
+ ({ f, $ }) => [
71
+ f.user({ id: $.userId })(() => [
72
+ userModel.fragment({}),
73
+ ]),
74
+ ],
75
+ ({ select }) =>
76
+ select(["$.user"], (result) =>
77
+ result.safeUnwrap(([user]) => userModel.normalize(user)),
78
+ ),
79
+ ),
80
+ );
81
+ ```
82
+
83
+ ### Writing Operations (Composed)
84
+
85
+ Composed operations combine multiple slices:
86
+
87
+ ```typescript
88
+ export const getUserQuery = gql.default(({ query }, { $var }) =>
89
+ query.composed(
90
+ {
91
+ operationName: "GetUser",
92
+ variables: [$var("id").scalar("ID:!")],
93
+ },
94
+ ({ $ }) => ({
95
+ user: userSlice.embed({ userId: $.id }),
96
+ }),
97
+ ),
98
+ );
99
+ ```
100
+
101
+ ### Writing Operations (Inline)
102
+
103
+ Inline operations define field selections directly:
104
+
105
+ ```typescript
106
+ export const getUserInline = gql.default(({ query }, { $var }) =>
107
+ query.inline(
108
+ {
109
+ operationName: "GetUserInline",
110
+ variables: [$var("id").scalar("ID:!")],
111
+ },
112
+ ({ f, $ }) => [
113
+ f.user({ id: $.id })(({ f }) => [
114
+ f.id(),
115
+ f.name(),
116
+ ]),
117
+ ],
118
+ ),
119
+ );
120
+ ```
121
+
122
+ ## Variable Type Syntax
123
+
124
+ Variables are declared using a string-based type syntax:
125
+
126
+ | Syntax | Meaning | GraphQL Equivalent |
127
+ |--------|---------|-------------------|
128
+ | `"ID:!"` | Required ID | `ID!` |
129
+ | `"String:?"` | Optional String | `String` |
130
+ | `"Int:![]!"` | Required list of required Int | `[Int!]!` |
131
+ | `"String:![]?"` | Optional list of required Strings | `[String!]` |
132
+ | `"MyInput:!"` | Required custom input type | `MyInput!` |
133
+
134
+ ## Field Selection Patterns
135
+
136
+ | Pattern | Description |
137
+ |---------|-------------|
138
+ | `f.id()` | Basic field selection |
139
+ | `f.posts({ limit: 10 })` | Field with arguments |
140
+ | `f.posts()(({ f }) => [...])` | Nested selection (curried) |
141
+ | `f.id(null, { alias: "uuid" })` | Field with alias |
142
+ | `f.email({ if: $.includeEmail })` | Conditional field |
143
+ | `userModel.fragment({})` | Use model fragment |
144
+
145
+ ## Defining Custom Scalars
146
+
147
+ Scalars define the TypeScript types for GraphQL scalar values:
148
+
149
+ ```typescript
150
+ import { defineScalar } from "@soda-gql/core";
151
+
152
+ export const scalar = {
153
+ // Built-in scalars
154
+ ...defineScalar<"ID", string, string>("ID"),
155
+ ...defineScalar<"String", string, string>("String"),
156
+ ...defineScalar<"Int", number, number>("Int"),
157
+ ...defineScalar<"Float", number, number>("Float"),
158
+ ...defineScalar<"Boolean", boolean, boolean>("Boolean"),
159
+
160
+ // Custom scalars - defineScalar<Name, InputType, OutputType>(name)
161
+ ...defineScalar<"DateTime", string, Date>("DateTime"),
162
+ ...defineScalar<"JSON", Record<string, unknown>, Record<string, unknown>>("JSON"),
163
+ } as const;
164
+ ```
165
+
166
+ Alternative callback syntax:
167
+
168
+ ```typescript
169
+ export const scalar = {
170
+ ...defineScalar("DateTime", ({ type }) => ({
171
+ input: type<string>(),
172
+ output: type<Date>(),
173
+ directives: {},
174
+ })),
175
+ } as const;
176
+ ```
177
+
178
+ ## Type Inference
179
+
180
+ Extract TypeScript types from soda-gql elements using `$infer`:
181
+
182
+ ```typescript
183
+ // Model types
184
+ type UserInput = typeof userModel.$infer.input;
185
+ type UserOutputRaw = typeof userModel.$infer.output.raw;
186
+ type UserOutputNormalized = typeof userModel.$infer.output.normalized;
187
+
188
+ // Operation types
189
+ type QueryVariables = typeof getUserQuery.$infer.input;
190
+ type QueryResult = typeof getUserQuery.$infer.output.projected;
191
+ ```
192
+
193
+ ## Metadata
194
+
195
+ Metadata allows you to attach runtime information to operations and slices. This is useful for HTTP headers, GraphQL extensions, and application-specific values.
196
+
197
+ ### Metadata Structure
198
+
199
+ All metadata types share three base properties:
200
+
201
+ | Property | Type | Purpose |
202
+ |----------|------|---------|
203
+ | `headers` | `Record<string, string>` | HTTP headers to include with the GraphQL request |
204
+ | `extensions` | `Record<string, unknown>` | GraphQL extensions in the request payload |
205
+ | `custom` | `Record<string, unknown>` | Application-specific values (auth requirements, cache settings, etc.) |
206
+
207
+ ### Defining Metadata
208
+
209
+ Metadata can be defined on both slices and operations:
210
+
211
+ ```typescript
212
+ // Slice with metadata
213
+ export const userSlice = gql.default(({ query }, { $var }) =>
214
+ query.slice(
215
+ {
216
+ variables: [$var("userId").scalar("ID:!")],
217
+ metadata: ({ $ }) => ({
218
+ headers: { "X-Request-ID": "user-query" },
219
+ custom: { requiresAuth: true, cacheTtl: 300 },
220
+ }),
221
+ },
222
+ ({ f, $ }) => [f.user({ id: $.userId })(({ f }) => [f.id()])],
223
+ ({ select }) => select(["$.user"], (user) => user),
224
+ ),
225
+ );
226
+
227
+ // Operation with metadata (can reference variables and document)
228
+ export const getUserQuery = gql.default(({ query }, { $var }) =>
229
+ query.composed(
230
+ {
231
+ operationName: "GetUser",
232
+ variables: [$var("id").scalar("ID:!")],
233
+ metadata: ({ $, document }) => ({
234
+ extensions: {
235
+ trackedVariables: [$var.getInner($.id)],
236
+ },
237
+ }),
238
+ },
239
+ ({ $ }) => ({
240
+ user: userSlice.embed({ userId: $.id }),
241
+ }),
242
+ ),
243
+ );
244
+ ```
245
+
246
+ ### MetadataAdapter
247
+
248
+ Use `createMetadataAdapter` to customize metadata behavior at the schema level:
249
+
250
+ ```typescript
251
+ import { createMetadataAdapter } from "@soda-gql/core/metadata";
252
+ import { createHash } from "crypto";
253
+
254
+ export const metadataAdapter = createMetadataAdapter({
255
+ // Default metadata applied to all operations
256
+ defaults: {
257
+ headers: { "X-GraphQL-Client": "soda-gql" },
258
+ },
259
+
260
+ // Transform metadata at build time (e.g., add persisted query hash)
261
+ transform: ({ document, metadata }) => ({
262
+ ...metadata,
263
+ extensions: {
264
+ ...metadata.extensions,
265
+ persistedQuery: {
266
+ version: 1,
267
+ sha256Hash: createHash("sha256").update(document).digest("hex"),
268
+ },
269
+ },
270
+ }),
271
+
272
+ // Custom merge strategy for slice metadata (optional)
273
+ mergeSliceMetadata: (operationMetadata, sliceMetadataList) => {
274
+ // Default: shallow merge where operation takes precedence
275
+ return { ...sliceMetadataList.reduce((acc, s) => ({ ...acc, ...s }), {}), ...operationMetadata };
276
+ },
277
+ });
278
+ ```
279
+
280
+ ### Metadata Merging
281
+
282
+ When an operation includes multiple slices, metadata is merged in this order:
283
+
284
+ 1. **Slice metadata** - Merged together (later slices override earlier ones)
285
+ 2. **Operation metadata** - Takes precedence over slice metadata
286
+ 3. **Schema defaults** - Applied first, overridden by operation/slice values
287
+
288
+ ## Runtime Exports
289
+
290
+ The `/runtime` subpath provides utilities for operation registration and retrieval:
291
+
292
+ ```typescript
293
+ import { gqlRuntime } from "@soda-gql/core/runtime";
294
+
295
+ // Retrieve registered operations (typically handled by build plugins)
296
+ const operation = gqlRuntime.getComposedOperation("canonicalId");
297
+ ```
298
+
299
+ ## TypeScript Support
300
+
301
+ This package requires TypeScript 5.x or later for full type inference support.
302
+
303
+ ## Related Packages
304
+
305
+ - [@soda-gql/cli](../cli) - Command-line interface for code generation
306
+ - [@soda-gql/config](../config) - Configuration management
307
+ - [@soda-gql/runtime](../runtime) - Runtime utilities for operation execution
308
+ - [@soda-gql/tsc-plugin](../tsc-plugin) - TypeScript compiler plugin
309
+
310
+ ## License
311
+
312
+ MIT