@leadcms/sdk 3.1.1 → 3.3.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 +104 -50
- package/dist/cli/bin/generate-env.d.ts +1 -1
- package/dist/cli/bin/generate-env.d.ts.map +1 -1
- package/dist/cli/bin/generate-env.js +2 -2
- package/dist/cli/bin/generate-env.js.map +1 -1
- package/dist/cli/bin/pull-all.js +5 -1
- package/dist/cli/bin/pull-all.js.map +1 -1
- package/dist/cli/bin/pull-comments.js +3 -1
- package/dist/cli/bin/pull-comments.js.map +1 -1
- package/dist/cli/bin/pull-content.js +2 -1
- package/dist/cli/bin/pull-content.js.map +1 -1
- package/dist/cli/bin/pull-media.js +3 -1
- package/dist/cli/bin/pull-media.js.map +1 -1
- package/dist/cli/index.js +8 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/cms.d.ts.map +1 -1
- package/dist/lib/cms.js +0 -38
- package/dist/lib/cms.js.map +1 -1
- package/dist/lib/content-merge.d.ts +80 -0
- package/dist/lib/content-merge.d.ts.map +1 -0
- package/dist/lib/content-merge.js +350 -0
- package/dist/lib/content-merge.js.map +1 -0
- package/dist/lib/content-transformation.d.ts +0 -10
- package/dist/lib/content-transformation.d.ts.map +1 -1
- package/dist/lib/content-transformation.js +18 -32
- package/dist/lib/content-transformation.js.map +1 -1
- package/dist/scripts/fetch-leadcms-comments.d.ts +6 -1
- package/dist/scripts/fetch-leadcms-comments.d.ts.map +1 -1
- package/dist/scripts/fetch-leadcms-comments.js +36 -8
- package/dist/scripts/fetch-leadcms-comments.js.map +1 -1
- package/dist/scripts/fetch-leadcms-content.d.ts +38 -1
- package/dist/scripts/fetch-leadcms-content.d.ts.map +1 -1
- package/dist/scripts/fetch-leadcms-content.js +337 -63
- package/dist/scripts/fetch-leadcms-content.js.map +1 -1
- package/dist/scripts/generate-env-js.d.ts +13 -1
- package/dist/scripts/generate-env-js.d.ts.map +1 -1
- package/dist/scripts/generate-env-js.js +33 -15
- package/dist/scripts/generate-env-js.js.map +1 -1
- package/dist/scripts/init-leadcms.d.ts +5 -1
- package/dist/scripts/init-leadcms.d.ts.map +1 -1
- package/dist/scripts/init-leadcms.js +11 -6
- package/dist/scripts/init-leadcms.js.map +1 -1
- package/dist/scripts/leadcms-helpers.d.ts +1 -6
- package/dist/scripts/leadcms-helpers.d.ts.map +1 -1
- package/dist/scripts/leadcms-helpers.js.map +1 -1
- package/dist/scripts/pull-all.d.ts +23 -0
- package/dist/scripts/pull-all.d.ts.map +1 -1
- package/dist/scripts/pull-all.js +83 -2
- package/dist/scripts/pull-all.js.map +1 -1
- package/dist/scripts/pull-comments.d.ts +5 -1
- package/dist/scripts/pull-comments.d.ts.map +1 -1
- package/dist/scripts/pull-comments.js +8 -1
- package/dist/scripts/pull-comments.js.map +1 -1
- package/dist/scripts/pull-content.d.ts +2 -0
- package/dist/scripts/pull-content.d.ts.map +1 -1
- package/dist/scripts/pull-content.js +7 -1
- package/dist/scripts/pull-content.js.map +1 -1
- package/dist/scripts/pull-media.d.ts +5 -1
- package/dist/scripts/pull-media.d.ts.map +1 -1
- package/dist/scripts/pull-media.js +8 -1
- package/dist/scripts/pull-media.js.map +1 -1
- package/dist/scripts/push-leadcms-content.d.ts +38 -0
- package/dist/scripts/push-leadcms-content.d.ts.map +1 -1
- package/dist/scripts/push-leadcms-content.js +17 -4
- package/dist/scripts/push-leadcms-content.js.map +1 -1
- package/dist/scripts/sse-watcher.d.ts.map +1 -1
- package/dist/scripts/sse-watcher.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ A comprehensive, framework-agnostic SDK and CLI tools for integrating with LeadC
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
### For Build-Time Usage (Most Common)
|
|
8
|
+
|
|
8
9
|
If you only use LeadCMS SDK during the build process (static site generation):
|
|
9
10
|
|
|
10
11
|
```bash
|
|
@@ -12,6 +13,7 @@ npm install --save-dev @leadcms/sdk
|
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
### For Runtime Usage
|
|
16
|
+
|
|
15
17
|
If you need LeadCMS SDK in your production application (SSR, API routes, live preview):
|
|
16
18
|
|
|
17
19
|
```bash
|
|
@@ -19,6 +21,7 @@ npm install @leadcms/sdk
|
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
### Global CLI Installation
|
|
24
|
+
|
|
22
25
|
For CLI tools and project setup:
|
|
23
26
|
|
|
24
27
|
```bash
|
|
@@ -28,18 +31,21 @@ npm install -g @leadcms/sdk
|
|
|
28
31
|
### When to Use Each Installation Method
|
|
29
32
|
|
|
30
33
|
**Development Dependency (`--save-dev`)** - Recommended for:
|
|
34
|
+
|
|
31
35
|
- ✅ Static Site Generators (Next.js, Astro, Gatsby, Nuxt)
|
|
32
36
|
- ✅ Build-time content fetching and processing
|
|
33
37
|
- ✅ Static route generation
|
|
34
38
|
- ✅ Content pre-processing during build
|
|
35
39
|
|
|
36
40
|
**Production Dependency (`--save`)** - Use when you need:
|
|
41
|
+
|
|
37
42
|
- 🔄 Server-Side Rendering (SSR) with dynamic content
|
|
38
43
|
- 🔄 API routes that fetch LeadCMS content at runtime
|
|
39
44
|
- 🔄 Live preview functionality in production
|
|
40
45
|
- 🔄 Runtime content loading and processing
|
|
41
46
|
|
|
42
47
|
**Global Installation (`-g`)** - Best for:
|
|
48
|
+
|
|
43
49
|
- 🛠️ CLI commands across multiple projects
|
|
44
50
|
- 🛠️ Project initialization and setup
|
|
45
51
|
- 🛠️ Content fetching and Docker template generation
|
|
@@ -49,18 +55,23 @@ npm install -g @leadcms/sdk
|
|
|
49
55
|
Get started with LeadCMS in 3 simple steps:
|
|
50
56
|
|
|
51
57
|
### 1. Initialize Your Project
|
|
58
|
+
|
|
52
59
|
```bash
|
|
53
60
|
npx leadcms init
|
|
54
61
|
```
|
|
62
|
+
|
|
55
63
|
This will:
|
|
64
|
+
|
|
56
65
|
- Connect to your LeadCMS instance
|
|
57
66
|
- Detect available entity types (content, media, comments)
|
|
58
67
|
- Create configuration files (`.env` and optionally `leadcms.config.json`)
|
|
59
68
|
|
|
60
69
|
### 2. Authenticate (for write access)
|
|
70
|
+
|
|
61
71
|
```bash
|
|
62
72
|
npx leadcms login
|
|
63
73
|
```
|
|
74
|
+
|
|
64
75
|
- **LeadCMS v1.2.88+**: Automatic device authentication via browser
|
|
65
76
|
- **Older versions**: Guided manual token extraction
|
|
66
77
|
- Saves your API token securely to `.env`
|
|
@@ -68,9 +79,11 @@ npx leadcms login
|
|
|
68
79
|
**Skip this step** if you only need read-only access to public content.
|
|
69
80
|
|
|
70
81
|
### 3. Download Your Content
|
|
82
|
+
|
|
71
83
|
```bash
|
|
72
84
|
npx leadcms pull
|
|
73
85
|
```
|
|
86
|
+
|
|
74
87
|
Downloads all content, media, and comments to your local project.
|
|
75
88
|
|
|
76
89
|
**That's it!** You're ready to use LeadCMS content in your application. See [Usage Examples](#usage-examples) below.
|
|
@@ -82,18 +95,21 @@ Downloads all content, media, and comments to your local project.
|
|
|
82
95
|
The LeadCMS SDK includes comprehensive CI/CD workflows for GitHub Actions that provide:
|
|
83
96
|
|
|
84
97
|
### 🧪 Automated Testing
|
|
98
|
+
|
|
85
99
|
- **Multi-Node Support**: Tests run on Node.js 18, 20, and 22
|
|
86
100
|
- **Coverage Reports**: Automatic coverage reporting with visual coverage diffs on PRs
|
|
87
101
|
- **Test Results**: Interactive test results displayed directly in GitHub Actions
|
|
88
102
|
- **JUnit XML**: Structured test output for integration with external tools
|
|
89
103
|
|
|
90
104
|
### 📊 Coverage Reporting
|
|
105
|
+
|
|
91
106
|
- **LCOV Reports**: Line and branch coverage tracking
|
|
92
107
|
- **PR Comments**: Automatic coverage comments on pull requests showing coverage changes
|
|
93
108
|
- **Coverage Artifacts**: HTML coverage reports archived for 30 days
|
|
94
109
|
- **Multiple Formats**: Coverage available in LCOV, HTML, and Clover formats
|
|
95
110
|
|
|
96
111
|
### 🔧 Quality Checks
|
|
112
|
+
|
|
97
113
|
- **TypeScript Compilation**: Ensures type safety across all Node.js versions
|
|
98
114
|
- **Package Validation**: Verifies package structure and CLI functionality
|
|
99
115
|
- **Docker Template Testing**: Validates generated Docker configurations
|
|
@@ -115,8 +131,8 @@ jobs:
|
|
|
115
131
|
- uses: actions/checkout@v4
|
|
116
132
|
- uses: actions/setup-node@v4
|
|
117
133
|
with:
|
|
118
|
-
node-version:
|
|
119
|
-
cache:
|
|
134
|
+
node-version: "20"
|
|
135
|
+
cache: "npm"
|
|
120
136
|
- run: npm ci
|
|
121
137
|
- run: npm test
|
|
122
138
|
```
|
|
@@ -136,13 +152,17 @@ npm run test:watch
|
|
|
136
152
|
|
|
137
153
|
### Test Coverage
|
|
138
154
|
|
|
139
|
-
The SDK maintains high test coverage with
|
|
140
|
-
|
|
155
|
+
The SDK maintains high test coverage with **481 tests across 30 test suites**, covering:
|
|
156
|
+
|
|
157
|
+
- 📄 Content retrieval, parsing, and transformation
|
|
141
158
|
- 🌍 Multi-language support and translations
|
|
142
159
|
- 📝 Draft content handling and user-specific overrides
|
|
143
160
|
- 🏗️ Build-time optimizations and caching
|
|
144
161
|
- 🔧 Configuration management and validation
|
|
145
162
|
- 🔄 Push/Pull synchronization with conflict detection
|
|
163
|
+
- 🗂️ Content rename, type change, and deletion handling
|
|
164
|
+
- 🗑️ Media deletion sync and file cleanup
|
|
165
|
+
- 🔁 Sync token migration and edge cases
|
|
146
166
|
- 🖥️ CLI command functionality with mocked API responses
|
|
147
167
|
|
|
148
168
|
### Testing with Mock Data
|
|
@@ -159,6 +179,7 @@ LEADCMS_USE_MOCK=true LEADCMS_MOCK_SCENARIO=mixedOperations npx leadcms push --d
|
|
|
159
179
|
```
|
|
160
180
|
|
|
161
181
|
**Available Mock Scenarios:**
|
|
182
|
+
|
|
162
183
|
- `allNew` - Local content that doesn't exist remotely (default)
|
|
163
184
|
- `noChanges` - All content is in sync
|
|
164
185
|
- `hasConflicts` - Remote content is newer than local
|
|
@@ -167,6 +188,7 @@ LEADCMS_USE_MOCK=true LEADCMS_MOCK_SCENARIO=mixedOperations npx leadcms push --d
|
|
|
167
188
|
- `missingContentTypes` - Content with unknown types
|
|
168
189
|
|
|
169
190
|
**Mock Mode Activation:**
|
|
191
|
+
|
|
170
192
|
- `NODE_ENV=test` - Automatically uses mock mode
|
|
171
193
|
- `LEADCMS_USE_MOCK=true` - Force mock mode
|
|
172
194
|
|
|
@@ -228,15 +250,15 @@ npx leadcms init
|
|
|
228
250
|
For advanced use cases, you can configure the SDK programmatically:
|
|
229
251
|
|
|
230
252
|
```typescript
|
|
231
|
-
import { configure } from
|
|
253
|
+
import { configure } from "@leadcms/sdk";
|
|
232
254
|
|
|
233
255
|
configure({
|
|
234
|
-
url:
|
|
235
|
-
apiKey:
|
|
236
|
-
defaultLanguage:
|
|
237
|
-
contentDir:
|
|
238
|
-
mediaDir:
|
|
239
|
-
enableDrafts: false
|
|
256
|
+
url: "https://your-leadcms-instance.com",
|
|
257
|
+
apiKey: "your-api-key",
|
|
258
|
+
defaultLanguage: "en",
|
|
259
|
+
contentDir: ".leadcms/content",
|
|
260
|
+
mediaDir: "public/media",
|
|
261
|
+
enableDrafts: false,
|
|
240
262
|
});
|
|
241
263
|
```
|
|
242
264
|
|
|
@@ -254,6 +276,7 @@ configure({
|
|
|
254
276
|
## CLI Usage
|
|
255
277
|
|
|
256
278
|
### Check SDK version
|
|
279
|
+
|
|
257
280
|
```bash
|
|
258
281
|
npx leadcms version
|
|
259
282
|
# or
|
|
@@ -263,11 +286,13 @@ npx leadcms --version
|
|
|
263
286
|
```
|
|
264
287
|
|
|
265
288
|
### Initialize configuration
|
|
289
|
+
|
|
266
290
|
```bash
|
|
267
291
|
npx leadcms init
|
|
268
292
|
```
|
|
269
293
|
|
|
270
294
|
Interactive setup wizard that:
|
|
295
|
+
|
|
271
296
|
1. **Connects to your LeadCMS instance** - Validates URL and checks for existing authentication
|
|
272
297
|
2. **Fetches configuration** - Retrieves default language and available languages from public `/api/config` endpoint
|
|
273
298
|
3. **Configures directories** - Sets content and media directories (defaults: `.leadcms/content`, `public/media`)
|
|
@@ -277,16 +302,19 @@ Interactive setup wizard that:
|
|
|
277
302
|
**Note:** The `/api/config` endpoint is public and works without authentication. For write operations and private content, run `leadcms login` after initialization.
|
|
278
303
|
|
|
279
304
|
### Login to LeadCMS
|
|
305
|
+
|
|
280
306
|
```bash
|
|
281
307
|
npx leadcms login
|
|
282
308
|
```
|
|
283
309
|
|
|
284
310
|
Authenticates with your LeadCMS instance:
|
|
311
|
+
|
|
285
312
|
- **Device Authentication** (LeadCMS v1.2.88+) - Opens a browser link for secure authentication
|
|
286
313
|
- **Manual Token** (older versions) - Guides you through extracting an API token
|
|
287
314
|
- **Saves token** - Automatically stores the token in your `.env` file
|
|
288
315
|
|
|
289
316
|
**When to use:**
|
|
317
|
+
|
|
290
318
|
- After running `leadcms init` if you need write access
|
|
291
319
|
- To update an expired or invalid token
|
|
292
320
|
- When switching between LeadCMS instances
|
|
@@ -315,7 +343,7 @@ Would you like to authenticate now? (Y/n): n
|
|
|
315
343
|
1. English (United States) [en-US] (default)
|
|
316
344
|
2. Russian (Russia) [ru-RU]
|
|
317
345
|
|
|
318
|
-
Default language code [en-US]:
|
|
346
|
+
Default language code [en-US]:
|
|
319
347
|
✓ Using default language: en-US
|
|
320
348
|
|
|
321
349
|
📦 Supported entity types:
|
|
@@ -341,12 +369,14 @@ Next steps:
|
|
|
341
369
|
```
|
|
342
370
|
|
|
343
371
|
The wizard creates:
|
|
372
|
+
|
|
344
373
|
- **`.env`** (or `.env` if exists) with `LEADCMS_URL`, `LEADCMS_DEFAULT_LANGUAGE`, and optionally `LEADCMS_API_KEY`
|
|
345
374
|
- **`leadcms.config.json`** only if custom directories are specified
|
|
346
375
|
|
|
347
376
|
**Anonymous Mode:** Perfect for static sites that only need public content. Omit the API key to skip authentication entirely.
|
|
348
377
|
|
|
349
378
|
### Generate Docker deployment templates
|
|
379
|
+
|
|
350
380
|
```bash
|
|
351
381
|
npx leadcms docker
|
|
352
382
|
# Creates Docker files for production and preview deployments
|
|
@@ -368,24 +398,36 @@ npx leadcms pull-media
|
|
|
368
398
|
|
|
369
399
|
# Pull only comments
|
|
370
400
|
npx leadcms pull-comments
|
|
401
|
+
|
|
402
|
+
# Reset and pull everything from scratch
|
|
403
|
+
npx leadcms pull --reset
|
|
371
404
|
```
|
|
372
405
|
|
|
373
406
|
> **Note:** `npx leadcms fetch` is still supported as an alias for backward compatibility.
|
|
374
407
|
|
|
375
408
|
What each command does:
|
|
409
|
+
|
|
376
410
|
- `npx leadcms pull` - Syncs content, media and comments into your project using the configured directories. Updates incremental sync tokens so subsequent runs are faster.
|
|
411
|
+
- `npx leadcms pull --reset` - Deletes all local content, media, comments, and sync tokens, then performs a full pull from scratch. Useful when local state has become inconsistent or after configuration changes.
|
|
377
412
|
- `npx leadcms pull-content` - Downloads only content entities (MDX/JSON files) and updates local metadata.
|
|
378
413
|
- `npx leadcms pull-media` - Downloads media files to your `mediaDir` (e.g., `public/media`). Use this when you changed media or want to refresh assets separately from content.
|
|
379
414
|
- `npx leadcms pull-comments` - Downloads comments to the comments directory (e.g., `.leadcms/comments/`). Useful when you only need comment updates.
|
|
380
415
|
|
|
381
|
-
|
|
416
|
+
**Intelligent sync handling:**
|
|
417
|
+
|
|
418
|
+
- **Incremental sync** — Sync tokens avoid re-downloading unchanged items on subsequent pulls.
|
|
419
|
+
- **Rename & type-change cleanup** — When content is renamed (slug change), moved to a different type, or switched format (MDX ↔ JSON), the old file is automatically removed before the new version is written.
|
|
420
|
+
- **Deleted content removal** — Content and media deleted on the server are removed locally during the next pull.
|
|
421
|
+
- **Sync token migration** — When upgrading from SDK ≤ 3.1, legacy sync tokens are automatically migrated to their new location inside each data directory.
|
|
382
422
|
|
|
383
423
|
### Push local content to LeadCMS
|
|
424
|
+
|
|
384
425
|
```bash
|
|
385
426
|
npx leadcms push [options]
|
|
386
427
|
```
|
|
387
428
|
|
|
388
429
|
Push your local content changes to LeadCMS. This command will:
|
|
430
|
+
|
|
389
431
|
- Analyze local MDX/JSON files and compare with remote content
|
|
390
432
|
- Detect new content, updates, and conflicts using `updatedAt` timestamps
|
|
391
433
|
- Prompt for confirmation before making changes
|
|
@@ -393,26 +435,29 @@ Push your local content changes to LeadCMS. This command will:
|
|
|
393
435
|
- Update local files with remote metadata (id, createdAt, updatedAt) after sync
|
|
394
436
|
|
|
395
437
|
**Options:**
|
|
396
|
-
- `--force` - Override remote changes (skip conflict check)
|
|
397
438
|
|
|
439
|
+
- `--force` - Override remote changes (skip conflict check)
|
|
398
440
|
|
|
399
441
|
**Content frontmatter / metadata (required and optional fields):**
|
|
442
|
+
|
|
400
443
|
```yaml
|
|
401
444
|
---
|
|
402
|
-
type: "article"
|
|
403
|
-
title: "Article Title"
|
|
404
|
-
slug: "article-slug"
|
|
405
|
-
language: "en"
|
|
445
|
+
type: "article" # required: Content type (must exist in LeadCMS)
|
|
446
|
+
title: "Article Title" # required: Content title
|
|
447
|
+
slug: "article-slug" # required: URL slug (unique per locale)
|
|
448
|
+
language: "en" # required: Content language
|
|
406
449
|
publishedAt: "2024-10-29T10:00:00Z" # optional: Publication date (omit to create a draft or schedule a future publish)
|
|
407
450
|
# updatedAt: "2024-10-29T10:00:00Z" # optional: maintained by the server; do not set for new content
|
|
408
451
|
---
|
|
409
452
|
```
|
|
410
453
|
|
|
411
454
|
Notes:
|
|
455
|
+
|
|
412
456
|
- `publishedAt` is optional. Omitting it is a valid way to create draft or scheduled content depending on your LeadCMS workflow.
|
|
413
457
|
- `updatedAt` is typically set and maintained by the LeadCMS server after content is created or updated. The SDK will use `updatedAt` when present for conflict detection, but you should not rely on it being set for brand-new local files.
|
|
414
458
|
|
|
415
459
|
### Check sync status
|
|
460
|
+
|
|
416
461
|
```bash
|
|
417
462
|
npx leadcms status
|
|
418
463
|
```
|
|
@@ -422,6 +467,7 @@ Shows the current sync status between local and remote **content** without makin
|
|
|
422
467
|
**Note:** The `status` command currently only supports content. Media and comments do not have sync status checking yet.
|
|
423
468
|
|
|
424
469
|
### Watch for real-time updates
|
|
470
|
+
|
|
425
471
|
```bash
|
|
426
472
|
npx leadcms watch
|
|
427
473
|
```
|
|
@@ -435,9 +481,9 @@ The SDK provides framework-agnostic data access. Most frameworks use it as a **d
|
|
|
435
481
|
export function generateStaticParams() {
|
|
436
482
|
// This runs at BUILD TIME, not runtime
|
|
437
483
|
const routes = getAllContentRoutes();
|
|
438
|
-
return routes.map(route => ({
|
|
484
|
+
return routes.map((route) => ({
|
|
439
485
|
slug: route.slugParts,
|
|
440
|
-
...(route.isDefaultLocale ? {} : { locale: route.locale })
|
|
486
|
+
...(route.isDefaultLocale ? {} : { locale: route.locale }),
|
|
441
487
|
}));
|
|
442
488
|
}
|
|
443
489
|
|
|
@@ -445,9 +491,9 @@ export function generateStaticParams() {
|
|
|
445
491
|
export function getStaticPaths() {
|
|
446
492
|
// This runs at BUILD TIME, not runtime
|
|
447
493
|
const routes = getAllContentRoutes();
|
|
448
|
-
return routes.map(route => ({
|
|
494
|
+
return routes.map((route) => ({
|
|
449
495
|
params: { slug: route.slug },
|
|
450
|
-
props: { locale: route.locale, path: route.path }
|
|
496
|
+
props: { locale: route.locale, path: route.path },
|
|
451
497
|
}));
|
|
452
498
|
}
|
|
453
499
|
|
|
@@ -456,11 +502,11 @@ exports.createPages = async ({ actions }) => {
|
|
|
456
502
|
const { createPage } = actions;
|
|
457
503
|
const routes = getAllContentRoutes();
|
|
458
504
|
|
|
459
|
-
routes.forEach(route => {
|
|
505
|
+
routes.forEach((route) => {
|
|
460
506
|
createPage({
|
|
461
507
|
path: route.path,
|
|
462
|
-
component: path.resolve(
|
|
463
|
-
context: { slug: route.slug, locale: route.locale }
|
|
508
|
+
component: path.resolve("./src/templates/content.js"),
|
|
509
|
+
context: { slug: route.slug, locale: route.locale },
|
|
464
510
|
});
|
|
465
511
|
});
|
|
466
512
|
};
|
|
@@ -468,18 +514,18 @@ exports.createPages = async ({ actions }) => {
|
|
|
468
514
|
// Runtime Usage Examples (Production dependency required)
|
|
469
515
|
|
|
470
516
|
// Next.js API Route (Runtime)
|
|
471
|
-
import { getCMSContentBySlugForLocale } from
|
|
517
|
+
import { getCMSContentBySlugForLocale } from "@leadcms/sdk";
|
|
472
518
|
|
|
473
519
|
export async function GET(request) {
|
|
474
520
|
// This runs at REQUEST TIME, needs production dependency
|
|
475
|
-
const content = getCMSContentBySlugForLocale(
|
|
521
|
+
const content = getCMSContentBySlugForLocale("about", "en");
|
|
476
522
|
return Response.json(content);
|
|
477
523
|
}
|
|
478
524
|
|
|
479
525
|
// Express.js Server (Runtime)
|
|
480
|
-
app.get(
|
|
526
|
+
app.get("/api/content/:slug", (req, res) => {
|
|
481
527
|
// This runs at REQUEST TIME, needs production dependency
|
|
482
|
-
const content = getCMSContentBySlugForLocale(req.params.slug,
|
|
528
|
+
const content = getCMSContentBySlugForLocale(req.params.slug, "en");
|
|
483
529
|
res.json(content);
|
|
484
530
|
});
|
|
485
531
|
```
|
|
@@ -491,17 +537,17 @@ app.get('/api/content/:slug', (req, res) => {
|
|
|
491
537
|
Get content from your LeadCMS instance:
|
|
492
538
|
|
|
493
539
|
```typescript
|
|
494
|
-
import {
|
|
540
|
+
import {
|
|
495
541
|
getCMSContentBySlugForLocale,
|
|
496
542
|
getAllContentSlugsForLocale,
|
|
497
|
-
getAllContentRoutes
|
|
498
|
-
} from
|
|
543
|
+
getAllContentRoutes,
|
|
544
|
+
} from "@leadcms/sdk";
|
|
499
545
|
|
|
500
546
|
// Get single content item
|
|
501
|
-
const content = getCMSContentBySlugForLocale(
|
|
547
|
+
const content = getCMSContentBySlugForLocale("about-us", "en");
|
|
502
548
|
|
|
503
549
|
// Get all content slugs
|
|
504
|
-
const slugs = getAllContentSlugsForLocale(
|
|
550
|
+
const slugs = getAllContentSlugsForLocale("en");
|
|
505
551
|
|
|
506
552
|
// Get all routes for static generation
|
|
507
553
|
const routes = getAllContentRoutes();
|
|
@@ -514,21 +560,22 @@ const routes = getAllContentRoutes();
|
|
|
514
560
|
The SDK automatically detects preview slugs and enables draft content access without requiring explicit configuration:
|
|
515
561
|
|
|
516
562
|
```typescript
|
|
517
|
-
import { getCMSContentBySlugForLocale } from
|
|
563
|
+
import { getCMSContentBySlugForLocale } from "@leadcms/sdk";
|
|
518
564
|
|
|
519
565
|
// Normal slug - only returns published content
|
|
520
|
-
const published = getCMSContentBySlugForLocale(
|
|
566
|
+
const published = getCMSContentBySlugForLocale("home", "en");
|
|
521
567
|
// Returns: null if content has no publishedAt
|
|
522
568
|
|
|
523
569
|
// Preview slug with GUID - automatically enables draft access
|
|
524
570
|
const preview = getCMSContentBySlugForLocale(
|
|
525
|
-
|
|
526
|
-
|
|
571
|
+
"home-550e8400-e29b-41d4-a716-446655440000",
|
|
572
|
+
"en",
|
|
527
573
|
);
|
|
528
574
|
// Returns: draft content even without publishedAt
|
|
529
575
|
```
|
|
530
576
|
|
|
531
577
|
**How it works:**
|
|
578
|
+
|
|
532
579
|
- When a slug contains a GUID pattern (e.g., `home-{userUid}`), the SDK automatically:
|
|
533
580
|
1. Detects the GUID suffix
|
|
534
581
|
2. Extracts the base slug and userUid
|
|
@@ -536,6 +583,7 @@ const preview = getCMSContentBySlugForLocale(
|
|
|
536
583
|
4. Returns user's draft version or falls back to base content
|
|
537
584
|
|
|
538
585
|
**Benefits:**
|
|
586
|
+
|
|
539
587
|
- ✅ Zero configuration - works automatically with LeadCMS preview URLs
|
|
540
588
|
- 🔒 Secure - only preview slugs (with valid GUID) can access drafts
|
|
541
589
|
- 🔄 Backward compatible - normal slugs continue to require `publishedAt`
|
|
@@ -570,18 +618,15 @@ npx leadcms pull-comments
|
|
|
570
618
|
```
|
|
571
619
|
|
|
572
620
|
```typescript
|
|
573
|
-
import {
|
|
574
|
-
getCommentsForContent,
|
|
575
|
-
getCommentsTreeForContent
|
|
576
|
-
} from '@leadcms/sdk';
|
|
621
|
+
import { getCommentsForContent, getCommentsTreeForContent } from "@leadcms/sdk";
|
|
577
622
|
|
|
578
623
|
// Get flat list of comments
|
|
579
624
|
const comments = getCommentsForContent(contentId);
|
|
580
625
|
|
|
581
626
|
// Get comments as tree for threading
|
|
582
627
|
const tree = getCommentsTreeForContent(contentId, undefined, {
|
|
583
|
-
sortOrder:
|
|
584
|
-
replySortOrder:
|
|
628
|
+
sortOrder: "newest",
|
|
629
|
+
replySortOrder: "oldest",
|
|
585
630
|
});
|
|
586
631
|
```
|
|
587
632
|
|
|
@@ -598,6 +643,7 @@ npx leadcms docker
|
|
|
598
643
|
```
|
|
599
644
|
|
|
600
645
|
This creates:
|
|
646
|
+
|
|
601
647
|
- `Dockerfile` - Production static site deployment
|
|
602
648
|
- `nginx.conf` - Optimized nginx configuration
|
|
603
649
|
- `scripts/inject-runtime-env.sh` - Runtime environment injection
|
|
@@ -650,6 +696,7 @@ docker run -p 80:80 \
|
|
|
650
696
|
## Debugging
|
|
651
697
|
|
|
652
698
|
### Debug Logging
|
|
699
|
+
|
|
653
700
|
Control SDK logging verbosity with environment variables:
|
|
654
701
|
|
|
655
702
|
```bash
|
|
@@ -663,32 +710,35 @@ NODE_ENV=production npm run build
|
|
|
663
710
|
Debug mode is automatically enabled when `NODE_ENV=development` or `LEADCMS_DEBUG=true`.
|
|
664
711
|
|
|
665
712
|
### Error Handling
|
|
713
|
+
|
|
666
714
|
The SDK provides detailed error information for missing configuration files:
|
|
667
715
|
|
|
668
716
|
```typescript
|
|
669
|
-
import { loadContentConfig, loadContentConfigStrict } from
|
|
717
|
+
import { loadContentConfig, loadContentConfigStrict } from "@leadcms/sdk";
|
|
670
718
|
|
|
671
719
|
// Graceful handling - returns null for missing files
|
|
672
|
-
const config = loadContentConfig(
|
|
720
|
+
const config = loadContentConfig("layout"); // Returns null if missing
|
|
673
721
|
|
|
674
722
|
// Strict handling - throws detailed errors for debugging
|
|
675
723
|
try {
|
|
676
|
-
const config = loadContentConfigStrict(
|
|
724
|
+
const config = loadContentConfigStrict("layout");
|
|
677
725
|
} catch (error) {
|
|
678
|
-
console.log(
|
|
679
|
-
console.log(
|
|
680
|
-
console.log(
|
|
726
|
+
console.log("Missing configuration:", error.configName);
|
|
727
|
+
console.log("Expected locale:", error.locale);
|
|
728
|
+
console.log("Full error:", error.message);
|
|
681
729
|
// Error message includes: configName, locale, and expected file path
|
|
682
730
|
}
|
|
683
731
|
```
|
|
684
732
|
|
|
685
733
|
**Error Details Include:**
|
|
734
|
+
|
|
686
735
|
- `configName` - The specific configuration name that was requested
|
|
687
736
|
- `locale` - The locale that was being loaded
|
|
688
737
|
- `message` - Full descriptive error including expected file path
|
|
689
738
|
- Clear console logging of missing files with exact paths
|
|
690
739
|
|
|
691
740
|
### Performance Tips
|
|
741
|
+
|
|
692
742
|
- ✅ Use configuration files instead of programmatic configuration for better caching
|
|
693
743
|
- ✅ The SDK caches file reads automatically - no manual optimization needed
|
|
694
744
|
- ✅ In production builds, logging is minimal to reduce noise
|
|
@@ -702,18 +752,22 @@ try {
|
|
|
702
752
|
- **[Documentation Index](./docs/README)** - Central hub for all documentation
|
|
703
753
|
|
|
704
754
|
#### Content & Media
|
|
755
|
+
|
|
705
756
|
- **[Content Management](./docs/CONTENT_MANAGEMENT.md)** - Retrieving, organizing, and working with content
|
|
706
757
|
- **[Media Management](./docs/MEDIA_MANAGEMENT.md)** - Handling media files and optimization
|
|
707
758
|
- **[Draft Handling](./docs/DRAFT_HANDLING.md)** - Working with draft content and user-specific drafts
|
|
708
759
|
|
|
709
760
|
#### Comments
|
|
761
|
+
|
|
710
762
|
- **[Comment Tree Guide](./docs/COMMENT_TREE.md)** - Building threaded comment interfaces with sorting and filtering
|
|
711
763
|
|
|
712
764
|
#### Setup & Configuration
|
|
765
|
+
|
|
713
766
|
- **[Interactive Init](./docs/INTERACTIVE_INIT.md)** - Setup wizard and authentication
|
|
714
767
|
- **[Public API Mode](./docs/PUBLIC_API_MODE.md)** - Security-first approach and operation modes
|
|
715
768
|
|
|
716
769
|
#### Development
|
|
770
|
+
|
|
717
771
|
- **[Development Guide](./docs/DEVELOPMENT.md)** - Local development, testing, and debugging
|
|
718
772
|
- **[GitHub Actions](./docs/GITHUB_ACTIONS.md)** - CI/CD setup and automated publishing
|
|
719
773
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-env.d.ts","sourceRoot":"","sources":["../../../src/cli/bin/generate-env.ts"],"names":[],"mappings":";AACA;;GAEG
|
|
1
|
+
{"version":3,"file":"generate-env.d.ts","sourceRoot":"","sources":["../../../src/cli/bin/generate-env.ts"],"names":[],"mappings":";AACA;;GAEG"}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* LeadCMS Generate Env CLI Entry Point
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { generateEnv } from '../../scripts/generate-env-js.js';
|
|
6
|
+
generateEnv();
|
|
7
7
|
//# sourceMappingURL=generate-env.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-env.js","sourceRoot":"","sources":["../../../src/cli/bin/generate-env.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,
|
|
1
|
+
{"version":3,"file":"generate-env.js","sourceRoot":"","sources":["../../../src/cli/bin/generate-env.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAE/D,WAAW,EAAE,CAAC"}
|
package/dist/cli/bin/pull-all.js
CHANGED
|
@@ -7,6 +7,7 @@ const args = process.argv.slice(2);
|
|
|
7
7
|
// Parse target ID or slug
|
|
8
8
|
let targetId;
|
|
9
9
|
let targetSlug;
|
|
10
|
+
let reset = false;
|
|
10
11
|
const idIndex = args.findIndex(arg => arg === '--id');
|
|
11
12
|
if (idIndex !== -1 && args[idIndex + 1]) {
|
|
12
13
|
targetId = args[idIndex + 1];
|
|
@@ -15,7 +16,10 @@ const slugIndex = args.findIndex(arg => arg === '--slug');
|
|
|
15
16
|
if (slugIndex !== -1 && args[slugIndex + 1]) {
|
|
16
17
|
targetSlug = args[slugIndex + 1];
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
if (args.includes('--reset')) {
|
|
20
|
+
reset = true;
|
|
21
|
+
}
|
|
22
|
+
pullAll({ targetId, targetSlug, reset }).catch((error) => {
|
|
19
23
|
console.error('Error running LeadCMS pull:', error.message);
|
|
20
24
|
process.exit(1);
|
|
21
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-all.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-all.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,0BAA0B;AAC1B,IAAI,QAA4B,CAAC;AACjC,IAAI,UAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"pull-all.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-all.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,0BAA0B;AAC1B,IAAI,QAA4B,CAAC;AACjC,IAAI,UAA8B,CAAC;AACnC,IAAI,KAAK,GAAG,KAAK,CAAC;AAElB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AACtD,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;IACxC,QAAQ,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC;AAC1D,IAAI,SAAS,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;IAC5C,UAAU,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;IAC7B,KAAK,GAAG,IAAI,CAAC;AACf,CAAC;AAED,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;IAC5D,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* LeadCMS Pull Comments CLI Entry Point
|
|
4
4
|
*/
|
|
5
5
|
import { pullComments } from '../../scripts/pull-comments.js';
|
|
6
|
-
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const reset = args.includes('--reset');
|
|
8
|
+
pullComments({ reset }).catch((error) => {
|
|
7
9
|
console.error('Error running LeadCMS pull comments:', error.message);
|
|
8
10
|
process.exit(1);
|
|
9
11
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-comments.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-comments.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"pull-comments.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-comments.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEvC,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;IAC3C,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -15,7 +15,8 @@ const slugIndex = args.findIndex(arg => arg === '--slug');
|
|
|
15
15
|
if (slugIndex !== -1 && args[slugIndex + 1]) {
|
|
16
16
|
targetSlug = args[slugIndex + 1];
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
const reset = args.includes('--reset');
|
|
19
|
+
pullContent({ targetId, targetSlug, reset }).catch((error) => {
|
|
19
20
|
console.error('Error running LeadCMS pull content:', error.message);
|
|
20
21
|
process.exit(1);
|
|
21
22
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-content.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-content.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,0BAA0B;AAC1B,IAAI,QAA4B,CAAC;AACjC,IAAI,UAA8B,CAAC;AAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AACtD,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;IACxC,QAAQ,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC;AAC1D,IAAI,SAAS,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;IAC5C,UAAU,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,WAAW,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"pull-content.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-content.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,0BAA0B;AAC1B,IAAI,QAA4B,CAAC;AACjC,IAAI,UAA8B,CAAC;AAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AACtD,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;IACxC,QAAQ,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC;AAC1D,IAAI,SAAS,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;IAC5C,UAAU,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEvC,WAAW,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;IAChE,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* LeadCMS Pull Media CLI Entry Point
|
|
4
4
|
*/
|
|
5
5
|
import { pullMedia } from '../../scripts/pull-media.js';
|
|
6
|
-
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const reset = args.includes('--reset');
|
|
8
|
+
pullMedia({ reset }).catch((error) => {
|
|
7
9
|
console.error('Error running LeadCMS pull media:', error.message);
|
|
8
10
|
process.exit(1);
|
|
9
11
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-media.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-media.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"pull-media.js","sourceRoot":"","sources":["../../../src/cli/bin/pull-media.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEvC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAU,EAAE,EAAE;IACxC,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|