@contentrain/query 5.1.3 → 5.1.5
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/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# `@contentrain/query`
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@contentrain/query)
|
|
4
|
+
[](https://agentskills.io)
|
|
4
5
|
[](https://github.com/Contentrain/ai/tree/main/packages/sdk/js)
|
|
5
6
|
[](https://ai.contentrain.io/packages/sdk)
|
|
6
7
|
|
|
@@ -340,24 +341,31 @@ const client = await clientModule.init()
|
|
|
340
341
|
const hero = client.singleton('hero').get()
|
|
341
342
|
```
|
|
342
343
|
|
|
343
|
-
## 🛠
|
|
344
|
+
## 🛠 Generation Commands
|
|
344
345
|
|
|
345
|
-
|
|
346
|
+
**Via the `contentrain` CLI (recommended for most users):**
|
|
346
347
|
|
|
347
348
|
```bash
|
|
348
|
-
|
|
349
|
+
contentrain generate # Generate once
|
|
350
|
+
contentrain generate --watch # Regenerate on model/content changes
|
|
351
|
+
contentrain generate --json # Machine-readable JSON for CI
|
|
349
352
|
```
|
|
350
353
|
|
|
351
|
-
|
|
354
|
+
**Via `contentrain-query` (programmatic / build tool flows):**
|
|
352
355
|
|
|
353
356
|
```bash
|
|
357
|
+
npx contentrain-query generate
|
|
354
358
|
npx contentrain-query generate --watch
|
|
359
|
+
npx contentrain-query generate --root /path/to/project
|
|
355
360
|
```
|
|
356
361
|
|
|
357
|
-
|
|
362
|
+
Or from TypeScript:
|
|
358
363
|
|
|
359
|
-
```
|
|
360
|
-
|
|
364
|
+
```ts
|
|
365
|
+
import { generate } from '@contentrain/query/generate'
|
|
366
|
+
|
|
367
|
+
const result = await generate({ projectRoot: process.cwd() })
|
|
368
|
+
console.log(result.generatedFiles.length)
|
|
361
369
|
```
|
|
362
370
|
|
|
363
371
|
## 📤 Package Exports
|
|
@@ -395,6 +403,16 @@ pnpm --filter @contentrain/query typecheck
|
|
|
395
403
|
pnpm exec oxlint packages/sdk/js/src packages/sdk/js/tests
|
|
396
404
|
```
|
|
397
405
|
|
|
406
|
+
## Agent Skill (embedded)
|
|
407
|
+
|
|
408
|
+
This package ships an embedded [Agent Skill](https://agentskills.io) at `skills/contentrain-query/SKILL.md`. AI coding agents can discover and load it for type-safe SDK usage guidance, including:
|
|
409
|
+
|
|
410
|
+
- QueryBuilder, SingletonAccessor, DictionaryAccessor, DocumentQuery APIs
|
|
411
|
+
- Local mode vs CDN mode differences
|
|
412
|
+
- Framework-specific bundler configuration (Vite, Next.js, Nuxt, SvelteKit, Metro)
|
|
413
|
+
|
|
414
|
+
The skill is available via the `@contentrain/query/skills/*` subpath export.
|
|
415
|
+
|
|
398
416
|
## 🔗 Related Packages
|
|
399
417
|
|
|
400
418
|
- `contentrain` — CLI that runs project initialization, validation, serve, and generation flows
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/query",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Optional type-safe query SDK for Contentrain — generated TypeScript client for platform-independent JSON content",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"i18n",
|
|
23
23
|
"generated-client",
|
|
24
24
|
"platform-independent",
|
|
25
|
+
"agent-skills",
|
|
25
26
|
"json",
|
|
26
27
|
"vue",
|
|
27
28
|
"react",
|
|
@@ -52,17 +53,19 @@
|
|
|
52
53
|
"types": "./dist/cdn/index.d.ts",
|
|
53
54
|
"import": "./dist/cdn/index.mjs",
|
|
54
55
|
"require": "./dist/cdn/index.cjs"
|
|
55
|
-
}
|
|
56
|
+
},
|
|
57
|
+
"./skills/*": "./skills/*"
|
|
56
58
|
},
|
|
57
59
|
"main": "./dist/index.cjs",
|
|
58
60
|
"module": "./dist/index.mjs",
|
|
59
61
|
"types": "./dist/index.d.ts",
|
|
60
62
|
"sideEffects": false,
|
|
61
63
|
"files": [
|
|
62
|
-
"dist"
|
|
64
|
+
"dist",
|
|
65
|
+
"skills"
|
|
63
66
|
],
|
|
64
67
|
"dependencies": {
|
|
65
|
-
"@contentrain/types": "0.
|
|
68
|
+
"@contentrain/types": "0.5.0"
|
|
66
69
|
},
|
|
67
70
|
"devDependencies": {
|
|
68
71
|
"@types/node": "^22.0.0",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contentrain-query
|
|
3
|
+
description: "Use the @contentrain/query SDK to query Contentrain content with type safety. Use when importing from #contentrain, using QueryBuilder, SingletonAccessor, DictionaryAccessor, DocumentQuery, or CDN client."
|
|
4
|
+
metadata:
|
|
5
|
+
author: Contentrain
|
|
6
|
+
version: "1.0.0"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Contentrain Query SDK
|
|
10
|
+
|
|
11
|
+
`@contentrain/query` is a Prisma-pattern generated client that provides type-safe access to Contentrain content. Generated output lives in `.contentrain/client/` — never edit it manually.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx contentrain generate # Generate client from models
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { query, singleton, dictionary, document } from '#contentrain'
|
|
21
|
+
|
|
22
|
+
// Collection
|
|
23
|
+
const posts = query('blog-post').where('category', 'eq', 'engineering').sort('published_at', 'desc').limit(10).all()
|
|
24
|
+
|
|
25
|
+
// Singleton
|
|
26
|
+
const hero = singleton('hero').get()
|
|
27
|
+
|
|
28
|
+
// Dictionary
|
|
29
|
+
const t = dictionary('ui-texts').get()
|
|
30
|
+
const loginLabel = t['auth.login.button']
|
|
31
|
+
|
|
32
|
+
// Document
|
|
33
|
+
const doc = document('blog-post').bySlug('getting-started')
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Runtime API
|
|
37
|
+
|
|
38
|
+
### QueryBuilder (collections)
|
|
39
|
+
|
|
40
|
+
| Method | Description |
|
|
41
|
+
|--------|-------------|
|
|
42
|
+
| `.all()` | Get all entries |
|
|
43
|
+
| `.first()` | Get first entry |
|
|
44
|
+
| `.count()` | Return count of matching entries |
|
|
45
|
+
| `.where(field, value)` | Equality filter (shorthand for `eq`) |
|
|
46
|
+
| `.where(field, op, value)` | Operator filter: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `contains` |
|
|
47
|
+
| `.sort(field, direction?)` | Sort (`asc`/`desc`, default `asc`) |
|
|
48
|
+
| `.limit(n)` | Limit results |
|
|
49
|
+
| `.offset(n)` | Skip results |
|
|
50
|
+
| `.include(relation)` | Resolve relation (1 level deep) |
|
|
51
|
+
|
|
52
|
+
### SingletonAccessor
|
|
53
|
+
|
|
54
|
+
| Method | Description |
|
|
55
|
+
|--------|-------------|
|
|
56
|
+
| `.get()` | Get the singleton data |
|
|
57
|
+
| `.include(relation)` | Resolve relation fields |
|
|
58
|
+
|
|
59
|
+
### DictionaryAccessor
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|--------|-------------|
|
|
63
|
+
| `.get()` | Get all key-value pairs |
|
|
64
|
+
| `.get(key)` | Get single value by key |
|
|
65
|
+
| `.get(key, params)` | Get with interpolation: `{placeholder}` → value |
|
|
66
|
+
|
|
67
|
+
### DocumentQuery
|
|
68
|
+
|
|
69
|
+
| Method | Description |
|
|
70
|
+
|--------|-------------|
|
|
71
|
+
| `.all()` | Get all documents |
|
|
72
|
+
| `.bySlug(slug)` | Get document by slug |
|
|
73
|
+
| `.first()` | Get first document |
|
|
74
|
+
| `.count()` | Return count of matching documents |
|
|
75
|
+
| `.where(field, value)` | Equality filter (shorthand) |
|
|
76
|
+
| `.where(field, op, value)` | Operator filter (same ops as QueryBuilder) |
|
|
77
|
+
| `.include(relation)` | Resolve relation fields |
|
|
78
|
+
|
|
79
|
+
## CDN Mode (Remote Data)
|
|
80
|
+
|
|
81
|
+
For server-side or client-side apps that fetch content from Contentrain Studio CDN:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { createContentrain } from '@contentrain/query/cdn'
|
|
85
|
+
|
|
86
|
+
const client = createContentrain({
|
|
87
|
+
projectId: '350696e8-...',
|
|
88
|
+
apiKey: 'crn_live_xxx',
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// All CDN queries are async (return Promise)
|
|
92
|
+
const posts = await client.collection('faq').locale('en').all()
|
|
93
|
+
const hero = await client.singleton('hero').locale('en').get()
|
|
94
|
+
const t = await client.dictionary('ui').locale('en').get()
|
|
95
|
+
const doc = await client.document('docs').locale('en').bySlug('intro')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Key Rules
|
|
99
|
+
|
|
100
|
+
- **Local mode** queries are **synchronous** — no `await` needed
|
|
101
|
+
- **CDN mode** queries are **async** — always `await`
|
|
102
|
+
- Relation resolution is **1 level deep** — no recursive resolution
|
|
103
|
+
- Locale fallback chain: explicit → config default → first available
|
|
104
|
+
- Generated files in `.contentrain/client/` are **immutable** — always regenerate, never edit
|
|
105
|
+
- Run `contentrain generate` after any model change
|
|
106
|
+
|
|
107
|
+
## Framework Integration
|
|
108
|
+
|
|
109
|
+
| Framework | Import | Notes |
|
|
110
|
+
|-----------|--------|-------|
|
|
111
|
+
| Nuxt 3 | `import { query } from '#contentrain'` | Server-only (server routes, plugins) |
|
|
112
|
+
| Next.js | `import { query } from '#contentrain'` | Works in RSC and API routes |
|
|
113
|
+
| Astro | `import { query } from '#contentrain'` | Works in `.astro` frontmatter |
|
|
114
|
+
| SvelteKit | `import { query } from '#contentrain'` | Works in `+page.server.ts` |
|
|
115
|
+
| Vue + Vite | `import { query } from '#contentrain'` | Requires Vite alias config |
|
|
116
|
+
| React + Vite | `import { query } from '#contentrain'` | Requires Vite alias config |
|
|
117
|
+
| Node.js | `import { query } from '#contentrain'` | ESM with subpath imports |
|
|
118
|
+
|
|
119
|
+
## References
|
|
120
|
+
|
|
121
|
+
| Reference | Description |
|
|
122
|
+
|-----------|-------------|
|
|
123
|
+
| [Bundler Configuration](references/bundler-config.md) | Vite, Next.js, Nuxt, SvelteKit, Metro alias setup |
|
|
124
|
+
|
|
125
|
+
## Related Skills
|
|
126
|
+
|
|
127
|
+
- **contentrain-generate** — Generate the SDK client before using it
|
|
128
|
+
- **contentrain-quality** — Content quality rules for entries you query
|
|
129
|
+
- **contentrain** — Core architecture and MCP tool catalog
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bundler-config
|
|
3
|
+
description: "Framework-specific bundler configuration for #contentrain subpath imports"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bundler Configuration for #contentrain
|
|
7
|
+
|
|
8
|
+
The `#contentrain` import requires subpath imports configuration in `package.json` and bundler-specific aliases.
|
|
9
|
+
|
|
10
|
+
## package.json (all frameworks)
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"imports": {
|
|
15
|
+
"#contentrain": "./.contentrain/client/index.mjs"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Vite (Vue, React, Svelte)
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// vite.config.ts
|
|
24
|
+
import { resolve } from 'node:path'
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
resolve: {
|
|
29
|
+
alias: {
|
|
30
|
+
'#contentrain': resolve(__dirname, '.contentrain/client/index.mjs')
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Next.js
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// next.config.mjs
|
|
40
|
+
import { resolve } from 'node:path'
|
|
41
|
+
|
|
42
|
+
export default {
|
|
43
|
+
webpack(config) {
|
|
44
|
+
config.resolve.alias['#contentrain'] = resolve(process.cwd(), '.contentrain/client/index.mjs')
|
|
45
|
+
return config
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Nuxt 3
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// nuxt.config.ts
|
|
54
|
+
import { resolve } from 'node:path'
|
|
55
|
+
|
|
56
|
+
export default defineNuxtConfig({
|
|
57
|
+
alias: {
|
|
58
|
+
'#contentrain': resolve(__dirname, '.contentrain/client/index.mjs')
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Important:** Treat `#contentrain` as **server-only** in Nuxt. Use in server routes, server plugins, and `useAsyncData` callbacks only.
|
|
64
|
+
|
|
65
|
+
## SvelteKit
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// svelte.config.js
|
|
69
|
+
import { resolve } from 'node:path'
|
|
70
|
+
|
|
71
|
+
export default {
|
|
72
|
+
kit: {
|
|
73
|
+
alias: {
|
|
74
|
+
'#contentrain': resolve('.contentrain/client/index.mjs')
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Expo / React Native (Metro)
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
// metro.config.js
|
|
84
|
+
const { getDefaultConfig } = require('expo/metro-config')
|
|
85
|
+
const path = require('path')
|
|
86
|
+
|
|
87
|
+
const config = getDefaultConfig(__dirname)
|
|
88
|
+
config.resolver.extraNodeModules = {
|
|
89
|
+
'#contentrain': path.resolve(__dirname, '.contentrain/client/index.cjs')
|
|
90
|
+
}
|
|
91
|
+
module.exports = config
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Bootstrap required:**
|
|
95
|
+
```javascript
|
|
96
|
+
// App.js (before any #contentrain import)
|
|
97
|
+
require('#contentrain').init()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Node.js (pure ESM)
|
|
101
|
+
|
|
102
|
+
No alias needed — Node.js natively supports `#imports` in `package.json`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"type": "module",
|
|
107
|
+
"imports": {
|
|
108
|
+
"#contentrain": "./.contentrain/client/index.mjs"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Node.js (CJS)
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"imports": {
|
|
118
|
+
"#contentrain": "./.contentrain/client/index.cjs"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Bootstrap required:
|
|
124
|
+
```javascript
|
|
125
|
+
const contentrain = require('#contentrain')
|
|
126
|
+
await contentrain.init()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Watch Mode
|
|
130
|
+
|
|
131
|
+
For development, run the generator in watch mode to auto-regenerate on model/content changes:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx contentrain generate --watch
|
|
135
|
+
```
|