@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
  [![npm version](https://img.shields.io/npm/v/%40contentrain%2Fquery?label=%40contentrain%2Fquery)](https://www.npmjs.com/package/@contentrain/query)
4
+ [![Agent Skills](https://img.shields.io/badge/Agent_Skill-contentrain--query-8B5CF6)](https://agentskills.io)
4
5
  [![GitHub source](https://img.shields.io/badge/source-Contentrain%2Fai-181717?logo=github)](https://github.com/Contentrain/ai/tree/main/packages/sdk/js)
5
6
  [![Docs](https://img.shields.io/badge/docs-ai.contentrain.io-0f172a)](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
- ## 🛠 CLI
344
+ ## 🛠 Generation Commands
344
345
 
345
- Generate once:
346
+ **Via the `contentrain` CLI (recommended for most users):**
346
347
 
347
348
  ```bash
348
- npx contentrain-query generate
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
- Generate in watch mode:
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
- Use a different project root:
362
+ Or from TypeScript:
358
363
 
359
- ```bash
360
- npx contentrain-query generate --root /path/to/project
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",
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.4.1"
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
+ ```