@contentstorage/core 1.1.1 → 1.2.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.
- package/README.md +349 -1
- package/dist/scripts/cli.d.ts +2 -0
- package/dist/scripts/cli.js +96 -0
- package/dist/scripts/generate-types.js +0 -1
- package/dist/scripts/pull-content.js +0 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1 +1,349 @@
|
|
|
1
|
-
# contentstorage
|
|
1
|
+
# @contentstorage/core
|
|
2
|
+
|
|
3
|
+
> A key-value based CMS core library that fetches content from ContentStorage and generates TypeScript types
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@contentstorage/core)
|
|
6
|
+
[](https://github.com/kaidohussar/contentstorage-core/blob/master/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Key-Value Storage** - Organize content with hierarchical dot-notation paths
|
|
11
|
+
- **Multi-Language Support** - Built-in support for 40+ languages
|
|
12
|
+
- **TypeScript First** - Automatic type generation from your content
|
|
13
|
+
- **Type Safety** - Full autocompletion and type checking for content access
|
|
14
|
+
- **Live Editor Integration** - Real-time content editing without page reload
|
|
15
|
+
- **Special Content Types** - Support for text, images, and variations
|
|
16
|
+
- **Variable Substitution** - Dynamic content with template variables
|
|
17
|
+
- **CLI Tools** - Easy content management with professional CLI
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @contentstorage/core
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @contentstorage/core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### 1. Create Configuration File
|
|
34
|
+
|
|
35
|
+
Create `contentstorage.config.js` in your project root:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
export default {
|
|
39
|
+
contentKey: 'your-content-key',
|
|
40
|
+
languageCodes: ['EN', 'FR', 'DE'],
|
|
41
|
+
contentDir: 'src/content/json',
|
|
42
|
+
typesOutputFile: 'src/content/content-types.d.ts'
|
|
43
|
+
};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Pull Content & Generate Types
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Pull content from ContentStorage CDN
|
|
50
|
+
npx contentstorage pull
|
|
51
|
+
|
|
52
|
+
# Generate TypeScript type definitions
|
|
53
|
+
npx contentstorage generate-types
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 3. Initialize and Use in Your App
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { initContentStorage, fetchContent, getText, getImage } from '@contentstorage/core';
|
|
60
|
+
|
|
61
|
+
// Initialize
|
|
62
|
+
initContentStorage({
|
|
63
|
+
contentKey: 'your-content-key',
|
|
64
|
+
languageCodes: ['EN', 'FR', 'DE']
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Fetch content for a language
|
|
68
|
+
await fetchContent('EN');
|
|
69
|
+
|
|
70
|
+
// Access content with full type safety
|
|
71
|
+
const title = getText('HomePage.title');
|
|
72
|
+
// → { contentId: 'HomePage.title', text: 'Welcome!' }
|
|
73
|
+
|
|
74
|
+
const heroImage = getImage('HomePage.hero');
|
|
75
|
+
// → { contentId: 'HomePage.hero', data: { url: '...', altText: '...' } }
|
|
76
|
+
|
|
77
|
+
// Use variables in text
|
|
78
|
+
const greeting = getText('HomePage.greeting', { name: 'John' });
|
|
79
|
+
// → { contentId: 'HomePage.greeting', text: 'Hello John!' }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## CLI Commands
|
|
83
|
+
|
|
84
|
+
### `contentstorage pull`
|
|
85
|
+
|
|
86
|
+
Pull content from ContentStorage CDN
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx contentstorage pull [options]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Options:**
|
|
93
|
+
- `--content-key <key>` - Content key for your project
|
|
94
|
+
- `--content-dir <dir>` - Directory to save content files
|
|
95
|
+
- `--lang <code>` - Language code (e.g., EN, FR)
|
|
96
|
+
- `--pending-changes` - Fetch pending/draft content
|
|
97
|
+
|
|
98
|
+
**Examples:**
|
|
99
|
+
```bash
|
|
100
|
+
npx contentstorage pull --content-key abc123
|
|
101
|
+
npx contentstorage pull --lang EN --pending-changes
|
|
102
|
+
npx contentstorage pull --content-dir src/content
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `contentstorage generate-types`
|
|
106
|
+
|
|
107
|
+
Generate TypeScript type definitions from content
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx contentstorage generate-types [options]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Options:**
|
|
114
|
+
- `--output <file>` - Output file for generated types
|
|
115
|
+
- `--content-key <key>` - Content key for your project
|
|
116
|
+
- `--lang <code>` - Language code (e.g., EN, FR)
|
|
117
|
+
- `--pending-changes` - Use pending/draft content
|
|
118
|
+
|
|
119
|
+
**Examples:**
|
|
120
|
+
```bash
|
|
121
|
+
npx contentstorage generate-types --output src/types.ts
|
|
122
|
+
npx contentstorage generate-types --content-key abc123 --lang EN
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `contentstorage --help`
|
|
126
|
+
|
|
127
|
+
Show all available commands and options
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx contentstorage --help
|
|
131
|
+
npx contentstorage pull --help
|
|
132
|
+
npx contentstorage generate-types --help
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Configuration
|
|
136
|
+
|
|
137
|
+
### Configuration File (`contentstorage.config.js`)
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
export default {
|
|
141
|
+
// Required: Unique content identifier
|
|
142
|
+
contentKey: 'your-content-key',
|
|
143
|
+
|
|
144
|
+
// Required: Array of language codes
|
|
145
|
+
languageCodes: ['EN', 'FR', 'DE', 'ES'],
|
|
146
|
+
|
|
147
|
+
// Optional: Local storage path (default: src/content/json)
|
|
148
|
+
contentDir: 'src/content/json',
|
|
149
|
+
|
|
150
|
+
// Optional: Types file path (default: src/content/content-types.ts)
|
|
151
|
+
typesOutputFile: 'src/content/content-types.d.ts',
|
|
152
|
+
|
|
153
|
+
// Optional: Fetch draft content
|
|
154
|
+
pendingChanges: false
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Supported Languages
|
|
159
|
+
|
|
160
|
+
The library supports 40+ languages including:
|
|
161
|
+
EN, FR, DE, ES, IT, PT, NL, PL, RU, TR, SV, NO, DA, FI, CS, SK, HU, RO, BG, HR, SL, SR, and more.
|
|
162
|
+
|
|
163
|
+
## API Reference
|
|
164
|
+
|
|
165
|
+
### Initialization
|
|
166
|
+
|
|
167
|
+
#### `initContentStorage(config)`
|
|
168
|
+
|
|
169
|
+
Initialize the content storage system.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
initContentStorage({
|
|
173
|
+
contentKey: string,
|
|
174
|
+
languageCodes: LanguageCode[]
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `setContentLanguage(config)`
|
|
179
|
+
|
|
180
|
+
Set content for a specific language.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
setContentLanguage({
|
|
184
|
+
languageCode: LanguageCode,
|
|
185
|
+
contentJson: object
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Content Retrieval
|
|
190
|
+
|
|
191
|
+
#### `getText<Path>(contentId, variables?)`
|
|
192
|
+
|
|
193
|
+
Get localized text with optional variable substitution.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const result = getText('HomePage.title');
|
|
197
|
+
// → { contentId: 'HomePage.title', text: 'Welcome!' }
|
|
198
|
+
|
|
199
|
+
const greeting = getText('HomePage.greeting', { name: 'John', count: 5 });
|
|
200
|
+
// → { contentId: 'HomePage.greeting', text: 'Hello John, you have 5 items' }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Returns:** `{ contentId: string, text: string }`
|
|
204
|
+
|
|
205
|
+
#### `getImage(contentId)`
|
|
206
|
+
|
|
207
|
+
Get image content with CDN URL.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const image = getImage('HomePage.hero');
|
|
211
|
+
// → {
|
|
212
|
+
// contentId: 'HomePage.hero',
|
|
213
|
+
// data: {
|
|
214
|
+
// contentstorage_type: 'image',
|
|
215
|
+
// url: 'https://cdn.contentstorage.app/...',
|
|
216
|
+
// altText: 'Hero image'
|
|
217
|
+
// }
|
|
218
|
+
// }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Returns:** `{ contentId: string, data: ImageObject } | undefined`
|
|
222
|
+
|
|
223
|
+
#### `getVariation(contentId, variationKey?, variables?)`
|
|
224
|
+
|
|
225
|
+
Get content variation for A/B testing.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const cta = getVariation('HomePage.cta', 'mobile');
|
|
229
|
+
// → { contentId: 'HomePage.cta', text: 'Tap Now' }
|
|
230
|
+
|
|
231
|
+
// Defaults to 'default' variation if not specified
|
|
232
|
+
const ctaDefault = getVariation('HomePage.cta');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Returns:** `{ contentId: string, text: string }`
|
|
236
|
+
|
|
237
|
+
### Content Fetching
|
|
238
|
+
|
|
239
|
+
#### `fetchContent(languageCode)`
|
|
240
|
+
|
|
241
|
+
Fetch content from ContentStorage CDN.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
await fetchContent('EN');
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## TypeScript Integration
|
|
248
|
+
|
|
249
|
+
The library uses interface augmentation for type-safe content access:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// After running: npx contentstorage generate-types
|
|
253
|
+
// The generated types augment the ContentStructure interface
|
|
254
|
+
|
|
255
|
+
import { getText } from '@contentstorage/core';
|
|
256
|
+
|
|
257
|
+
// TypeScript knows all available content paths
|
|
258
|
+
const title = getText('HomePage.title'); // ✅ Autocomplete works
|
|
259
|
+
const invalid = getText('Invalid.path'); // ❌ TypeScript error
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Content Structure
|
|
263
|
+
|
|
264
|
+
Content is organized in a hierarchical key-value structure:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"HomePage": {
|
|
269
|
+
"title": "Welcome to Our App",
|
|
270
|
+
"greeting": "Hello {name}, you have {count} items",
|
|
271
|
+
"hero": {
|
|
272
|
+
"contentstorage_type": "image",
|
|
273
|
+
"url": "hero.jpg",
|
|
274
|
+
"altText": "Hero image"
|
|
275
|
+
},
|
|
276
|
+
"cta": {
|
|
277
|
+
"contentstorage_type": "variation",
|
|
278
|
+
"data": {
|
|
279
|
+
"default": "Click Here",
|
|
280
|
+
"mobile": "Tap Now",
|
|
281
|
+
"desktop": "Click to Continue"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Access with dot notation:**
|
|
289
|
+
- `getText('HomePage.title')` → "Welcome to Our App"
|
|
290
|
+
- `getText('HomePage.greeting', { name: 'John', count: 5 })` → "Hello John, you have 5 items"
|
|
291
|
+
- `getImage('HomePage.hero')` → Image object with CDN URL
|
|
292
|
+
- `getVariation('HomePage.cta', 'mobile')` → "Tap Now"
|
|
293
|
+
|
|
294
|
+
## Package.json Scripts
|
|
295
|
+
|
|
296
|
+
Add these scripts to your `package.json` for convenience:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"scripts": {
|
|
301
|
+
"content:pull": "contentstorage pull",
|
|
302
|
+
"content:types": "contentstorage generate-types",
|
|
303
|
+
"content:sync": "npm run content:pull && npm run content:types",
|
|
304
|
+
"prebuild": "npm run content:sync"
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Then run:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
npm run content:pull # Pull content
|
|
313
|
+
npm run content:types # Generate types
|
|
314
|
+
npm run content:sync # Pull and generate in one command
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Integration with React
|
|
318
|
+
|
|
319
|
+
For React integration, use [@contentstorage/react](https://www.npmjs.com/package/@contentstorage/react) which builds on top of this core library and provides React-specific components and hooks.
|
|
320
|
+
|
|
321
|
+
## Live Editor Mode
|
|
322
|
+
|
|
323
|
+
When running in an iframe with live editor parameters, the library automatically:
|
|
324
|
+
- Loads the live editor script from CDN
|
|
325
|
+
- Tracks content usage via `window.memoryMap`
|
|
326
|
+
- Enables real-time content updates without page reload
|
|
327
|
+
|
|
328
|
+
## Requirements
|
|
329
|
+
|
|
330
|
+
- Node.js >= 18.0.0
|
|
331
|
+
- TypeScript >= 5.0.0 (for type generation)
|
|
332
|
+
|
|
333
|
+
## License
|
|
334
|
+
|
|
335
|
+
MIT
|
|
336
|
+
|
|
337
|
+
## Author
|
|
338
|
+
|
|
339
|
+
Kaido Hussar - [kaidohus@gmail.com](mailto:kaidohus@gmail.com)
|
|
340
|
+
|
|
341
|
+
## Links
|
|
342
|
+
|
|
343
|
+
- [Homepage](https://contentstorage.app)
|
|
344
|
+
- [GitHub](https://github.com/kaidohussar/contentstorage-core)
|
|
345
|
+
- [npm](https://www.npmjs.com/package/@contentstorage/core)
|
|
346
|
+
|
|
347
|
+
## Support
|
|
348
|
+
|
|
349
|
+
For issues and questions, please [open an issue](https://github.com/kaidohussar/contentstorage-core/issues) on GitHub.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { pullContent } from './pull-content.js';
|
|
4
|
+
import { generateTypes } from './generate-types.js';
|
|
5
|
+
const COMMANDS = {
|
|
6
|
+
pull: {
|
|
7
|
+
name: 'pull',
|
|
8
|
+
description: 'Pull content from Contentstorage CDN',
|
|
9
|
+
usage: 'contentstorage pull [options]',
|
|
10
|
+
options: [
|
|
11
|
+
' --content-key <key> Content key for your project',
|
|
12
|
+
' --content-dir <dir> Directory to save content files',
|
|
13
|
+
' --lang <code> Language code (e.g., EN, FR)',
|
|
14
|
+
' --pending-changes Fetch pending/draft content',
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
'generate-types': {
|
|
18
|
+
name: 'generate-types',
|
|
19
|
+
description: 'Generate TypeScript type definitions from content',
|
|
20
|
+
usage: 'contentstorage generate-types [options]',
|
|
21
|
+
options: [
|
|
22
|
+
' --output <file> Output file for generated types',
|
|
23
|
+
' --content-key <key> Content key for your project',
|
|
24
|
+
' --lang <code> Language code (e.g., EN, FR)',
|
|
25
|
+
' --pending-changes Use pending/draft content',
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
function showHelp() {
|
|
30
|
+
console.log(chalk.bold('\nContentStorage CLI'));
|
|
31
|
+
console.log(chalk.dim('Manage content and generate TypeScript types\n'));
|
|
32
|
+
console.log(chalk.bold('Usage:'));
|
|
33
|
+
console.log(' contentstorage <command> [options]\n');
|
|
34
|
+
console.log(chalk.bold('Commands:'));
|
|
35
|
+
Object.values(COMMANDS).forEach((cmd) => {
|
|
36
|
+
console.log(` ${chalk.cyan(cmd.name.padEnd(20))} ${cmd.description}`);
|
|
37
|
+
});
|
|
38
|
+
console.log(chalk.bold('\nOptions:'));
|
|
39
|
+
console.log(' --help Show help for a command\n');
|
|
40
|
+
console.log(chalk.dim('Examples:'));
|
|
41
|
+
console.log(chalk.dim(' contentstorage pull --content-key abc123'));
|
|
42
|
+
console.log(chalk.dim(' contentstorage generate-types --output types.ts'));
|
|
43
|
+
console.log(chalk.dim(' contentstorage pull --help # Show help for pull command\n'));
|
|
44
|
+
}
|
|
45
|
+
function showCommandHelp(commandName) {
|
|
46
|
+
const cmd = COMMANDS[commandName];
|
|
47
|
+
if (!cmd) {
|
|
48
|
+
console.error(chalk.red(`Unknown command: ${commandName}`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
console.log(chalk.bold(`\n${cmd.name}`));
|
|
52
|
+
console.log(chalk.dim(cmd.description + '\n'));
|
|
53
|
+
console.log(chalk.bold('Usage:'));
|
|
54
|
+
console.log(` ${cmd.usage}\n`);
|
|
55
|
+
if (cmd.options.length > 0) {
|
|
56
|
+
console.log(chalk.bold('Options:'));
|
|
57
|
+
cmd.options.forEach((opt) => console.log(opt));
|
|
58
|
+
console.log('');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function main() {
|
|
62
|
+
const args = process.argv.slice(2);
|
|
63
|
+
// No arguments - show help
|
|
64
|
+
if (args.length === 0) {
|
|
65
|
+
showHelp();
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
const command = args[0];
|
|
69
|
+
// Global --help flag
|
|
70
|
+
if (command === '--help' || command === '-h') {
|
|
71
|
+
showHelp();
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
// Command-specific --help
|
|
75
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
76
|
+
showCommandHelp(command);
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
// Route to commands
|
|
80
|
+
switch (command) {
|
|
81
|
+
case 'pull':
|
|
82
|
+
await pullContent();
|
|
83
|
+
break;
|
|
84
|
+
case 'generate-types':
|
|
85
|
+
await generateTypes();
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
console.error(chalk.red(`Unknown command: ${command}\n`));
|
|
89
|
+
console.log(chalk.dim('Run "contentstorage --help" for usage'));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
main().catch((error) => {
|
|
94
|
+
console.error(chalk.red('Unexpected error:'), error);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
});
|
package/package.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"name": "@contentstorage/core",
|
|
3
3
|
"author": "Kaido Hussar <kaidohus@gmail.com>",
|
|
4
4
|
"homepage": "https://contentstorage.app",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Fetch content from contentstorage and generate TypeScript types",
|
|
8
|
+
"license": "MIT",
|
|
8
9
|
"module": "dist/index.js",
|
|
9
10
|
"types": "dist/index.d.ts",
|
|
10
11
|
"bin": {
|
|
11
|
-
"
|
|
12
|
-
"generate-types": "dist/scripts/generate-types.js"
|
|
12
|
+
"contentstorage": "dist/scripts/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|