@spooky-sync/query-builder 0.0.1-canary.16 → 0.0.1-canary.18
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spooky-sync/query-builder",
|
|
3
|
-
"version": "0.0.1-canary.
|
|
3
|
+
"version": "0.0.1-canary.18",
|
|
4
4
|
"description": "Type-safe query builder for SurrealDB with relationship support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"surrealdb",
|
|
25
25
|
"query-builder",
|
|
26
26
|
"typescript",
|
|
27
|
-
"type-safe"
|
|
27
|
+
"type-safe",
|
|
28
|
+
"tanstack-intent"
|
|
28
29
|
],
|
|
29
30
|
"author": "",
|
|
30
31
|
"license": "MIT",
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spooky-query-builder
|
|
3
|
+
description: >-
|
|
4
|
+
Type-safe query builder for SurrealDB used by the Spooky framework. Use when
|
|
5
|
+
defining schemas (SchemaStructure), building queries with QueryBuilder, handling
|
|
6
|
+
relationships (one/many cardinality), or working with Spooky type helpers like
|
|
7
|
+
TableNames, GetTable, TableModel, and QueryResult.
|
|
8
|
+
metadata:
|
|
9
|
+
author: spooky-sync
|
|
10
|
+
version: "0.0.1"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Spooky Query Builder
|
|
14
|
+
|
|
15
|
+
`@spooky-sync/query-builder` provides the type-safe schema definition format and query builder used throughout the Spooky framework.
|
|
16
|
+
|
|
17
|
+
## Schema Definition
|
|
18
|
+
|
|
19
|
+
Spooky schemas are defined as `const` objects satisfying `SchemaStructure`. They are typically generated by the Spooky CLI (`spooky generate`), but can be written by hand.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import type { SchemaStructure } from '@spooky-sync/query-builder';
|
|
23
|
+
|
|
24
|
+
export const schema = {
|
|
25
|
+
tables: [
|
|
26
|
+
{
|
|
27
|
+
name: 'user',
|
|
28
|
+
columns: {
|
|
29
|
+
id: { type: 'string', optional: false },
|
|
30
|
+
email: { type: 'string', optional: false },
|
|
31
|
+
name: { type: 'string', optional: true },
|
|
32
|
+
},
|
|
33
|
+
primaryKey: ['id'],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'post',
|
|
37
|
+
columns: {
|
|
38
|
+
id: { type: 'string', optional: false },
|
|
39
|
+
title: { type: 'string', optional: false },
|
|
40
|
+
body: { type: 'string', optional: false },
|
|
41
|
+
author: { type: 'string', optional: false, recordId: true },
|
|
42
|
+
},
|
|
43
|
+
primaryKey: ['id'],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
relationships: [
|
|
47
|
+
{ from: 'post', field: 'author', to: 'user', cardinality: 'one' },
|
|
48
|
+
{ from: 'user', field: 'posts', to: 'post', cardinality: 'many' },
|
|
49
|
+
],
|
|
50
|
+
backends: {},
|
|
51
|
+
} as const satisfies SchemaStructure;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Column Types
|
|
55
|
+
|
|
56
|
+
The `type` field maps to TypeScript types:
|
|
57
|
+
|
|
58
|
+
| ValueType | TypeScript Type |
|
|
59
|
+
|-------------|-----------------|
|
|
60
|
+
| `'string'` | `string` |
|
|
61
|
+
| `'number'` | `number` |
|
|
62
|
+
| `'boolean'` | `boolean` |
|
|
63
|
+
| `'null'` | `null` |
|
|
64
|
+
| `'json'` | `unknown` |
|
|
65
|
+
|
|
66
|
+
Set `optional: true` to make a field nullable (`T | null`).
|
|
67
|
+
Set `recordId: true` to indicate a field stores a SurrealDB RecordId.
|
|
68
|
+
|
|
69
|
+
## QueryBuilder API
|
|
70
|
+
|
|
71
|
+
The `QueryBuilder` class provides a fluent, chainable API for constructing type-safe queries.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { QueryBuilder } from '@spooky-sync/query-builder';
|
|
75
|
+
|
|
76
|
+
// Create a query builder for the "post" table
|
|
77
|
+
const query = new QueryBuilder(schema, 'post')
|
|
78
|
+
.where({ author: 'user:alice' })
|
|
79
|
+
.select('id', 'title', 'body')
|
|
80
|
+
.orderBy('title', 'desc')
|
|
81
|
+
.limit(10)
|
|
82
|
+
.offset(0)
|
|
83
|
+
.related('author')
|
|
84
|
+
.build();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Chain Methods
|
|
88
|
+
|
|
89
|
+
| Method | Signature | Description |
|
|
90
|
+
|--------|-----------|-------------|
|
|
91
|
+
| `where` | `.where(conditions)` | Filter by field values. String IDs are auto-parsed to RecordId. |
|
|
92
|
+
| `select` | `.select(...fields)` | Pick specific fields. Can only be called once per query. |
|
|
93
|
+
| `orderBy` | `.orderBy(field, 'asc' \| 'desc')` | Sort results. Default direction is `'asc'`. |
|
|
94
|
+
| `limit` | `.limit(count)` | Limit the number of results. |
|
|
95
|
+
| `offset` | `.offset(count)` | Skip the first N results. |
|
|
96
|
+
| `one` | `.one()` | Return a single object instead of an array. Implicitly sets `limit(1)`. |
|
|
97
|
+
| `related` | `.related(field, modifier?)` | Include related data via subquery. See [Relationships](#relationships). |
|
|
98
|
+
| `build` | `.build()` | Finalize the query into a `FinalQuery` object. |
|
|
99
|
+
|
|
100
|
+
### Relationships
|
|
101
|
+
|
|
102
|
+
Use `.related()` to include related tables. Relationships must be declared in the schema.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// One-to-one: post has one author
|
|
106
|
+
new QueryBuilder(schema, 'post')
|
|
107
|
+
.related('author')
|
|
108
|
+
.build();
|
|
109
|
+
|
|
110
|
+
// One-to-many: user has many posts
|
|
111
|
+
new QueryBuilder(schema, 'user')
|
|
112
|
+
.related('posts')
|
|
113
|
+
.build();
|
|
114
|
+
|
|
115
|
+
// Nested relationship with modifier
|
|
116
|
+
new QueryBuilder(schema, 'user')
|
|
117
|
+
.related('posts', (q) => q.orderBy('title', 'asc').limit(5))
|
|
118
|
+
.build();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Cardinality is inferred from the schema. For `'one'` relationships, the result is an object. For `'many'`, it is an array.
|
|
122
|
+
|
|
123
|
+
## Type Helpers
|
|
124
|
+
|
|
125
|
+
See [references/type-helpers.md](references/type-helpers.md) for a full reference of all type utilities.
|
|
126
|
+
|
|
127
|
+
Key types:
|
|
128
|
+
|
|
129
|
+
- `TableNames<S>` — Union of all table name strings
|
|
130
|
+
- `GetTable<S, Name>` — Extract a table definition by name
|
|
131
|
+
- `TableModel<T>` — Convert a table's columns to a TypeScript object type
|
|
132
|
+
- `QueryResult<S, TableName, RelatedFields, IsOne>` — The full result type including related fields
|
|
133
|
+
- `BackendNames<S>`, `BackendRoutes<S, B>`, `RoutePayload<S, B, R>` — Backend/run type helpers
|
|
134
|
+
|
|
135
|
+
## Common Pitfalls
|
|
136
|
+
|
|
137
|
+
1. **String IDs are auto-converted**: When you pass `'user:alice'` in a `where()`, it is automatically parsed into a SurrealDB `RecordId`. If the string does not contain `:`, and the field is named `id`, the table name is prepended.
|
|
138
|
+
|
|
139
|
+
2. **`select()` can only be called once**: Calling it twice throws an error. Combine all fields in one call.
|
|
140
|
+
|
|
141
|
+
3. **Schema must use `as const satisfies SchemaStructure`**: Without `as const`, TypeScript cannot infer literal types for table names and relationships, and type safety is lost.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Type Helpers Reference
|
|
2
|
+
|
|
3
|
+
## Schema Types
|
|
4
|
+
|
|
5
|
+
### `SchemaStructure`
|
|
6
|
+
|
|
7
|
+
The top-level schema interface:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface SchemaStructure {
|
|
11
|
+
readonly tables: readonly {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly columns: Record<string, ColumnSchema>;
|
|
14
|
+
readonly primaryKey: readonly string[];
|
|
15
|
+
}[];
|
|
16
|
+
readonly relationships: readonly {
|
|
17
|
+
readonly from: string;
|
|
18
|
+
readonly field: string;
|
|
19
|
+
readonly to: string;
|
|
20
|
+
readonly cardinality: 'one' | 'many';
|
|
21
|
+
}[];
|
|
22
|
+
readonly backends: Record<string, HTTPOutboxBackendDefinition>;
|
|
23
|
+
readonly access?: Record<string, AccessDefinition>;
|
|
24
|
+
readonly buckets?: readonly BucketDefinitionSchema[];
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### `ColumnSchema`
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
interface ColumnSchema {
|
|
32
|
+
readonly type: 'string' | 'number' | 'boolean' | 'null' | 'json';
|
|
33
|
+
readonly optional: boolean;
|
|
34
|
+
readonly dateTime?: boolean;
|
|
35
|
+
readonly recordId?: boolean;
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Table Type Helpers
|
|
40
|
+
|
|
41
|
+
| Type | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `TableNames<S>` | Union of all table name strings from the schema |
|
|
44
|
+
| `GetTable<S, Name>` | Extract a specific table definition by name |
|
|
45
|
+
| `TableModel<T>` | Convert a table's `columns` record into a TypeScript object type |
|
|
46
|
+
| `TableFieldNames<T>` | Union of all column names for a table |
|
|
47
|
+
|
|
48
|
+
### Example
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
type MySchema = typeof schema;
|
|
52
|
+
type Tables = TableNames<MySchema>; // 'user' | 'post'
|
|
53
|
+
type UserTable = GetTable<MySchema, 'user'>; // The user table definition
|
|
54
|
+
type UserModel = TableModel<UserTable>; // { id: string; email: string; name: string | null }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Relationship Type Helpers
|
|
58
|
+
|
|
59
|
+
| Type | Description |
|
|
60
|
+
|------|-------------|
|
|
61
|
+
| `TableRelationships<S, TableName>` | All relationship definitions originating from a table |
|
|
62
|
+
| `RelationshipFields<S, TableName>` | Union of relationship field names for a table |
|
|
63
|
+
| `GetRelationship<S, TableName, Field>` | Get a specific relationship by table and field name |
|
|
64
|
+
|
|
65
|
+
## Result Type Helpers
|
|
66
|
+
|
|
67
|
+
| Type | Description |
|
|
68
|
+
|------|-------------|
|
|
69
|
+
| `QueryResult<S, TableName, RelatedFields, IsOne>` | The full query result type. If `IsOne` is `true`, returns a single object; otherwise an array. Includes related fields merged into the base model. |
|
|
70
|
+
| `RelatedFieldsMap` | A record mapping field names to `{ to, cardinality, relatedFields }` |
|
|
71
|
+
|
|
72
|
+
## Backend Type Helpers
|
|
73
|
+
|
|
74
|
+
| Type | Description |
|
|
75
|
+
|------|-------------|
|
|
76
|
+
| `BackendNames<S>` | Union of all backend names |
|
|
77
|
+
| `BackendRoutes<S, B>` | Union of all route paths for a backend |
|
|
78
|
+
| `RoutePayload<S, B, R>` | The typed payload object for a specific route |
|
|
79
|
+
| `BucketNames<S>` | Union of all bucket names |
|
|
80
|
+
| `BucketConfig<S, B>` | Configuration for a specific bucket |
|