@forjacms/client 1.2.6
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 +282 -0
- package/dist/angular/index.cjs +1 -0
- package/dist/angular/index.d.cts +140 -0
- package/dist/angular/index.d.mts +140 -0
- package/dist/angular/index.mjs +1 -0
- package/dist/client-BBAKLM2r.d.cts +1673 -0
- package/dist/client-DTfhjUoX.mjs +1 -0
- package/dist/client-d-QwwKUW.d.mts +1673 -0
- package/dist/client-eFMh_n88.cjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +104 -0
- package/dist/index.d.mts +104 -0
- package/dist/index.mjs +1 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# @forjacms/client
|
|
2
|
+
|
|
3
|
+
Typed TypeScript SDK for the Forja CMS content API. Works in Node.js, browsers, and edge runtimes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @forjacms/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ForjaClient } from '@forjacms/client';
|
|
15
|
+
|
|
16
|
+
const forja = new ForjaClient({
|
|
17
|
+
baseUrl: 'https://cms.example.com/api/v1',
|
|
18
|
+
apiKey: 'dk_read_...',
|
|
19
|
+
siteId: 'your-site-uuid',
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Blogs
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Published blogs (paginated)
|
|
29
|
+
const blogs = await forja.blogs.listPublished({ page: 1, pageSize: 10 });
|
|
30
|
+
console.log(blogs.data); // BlogListItem[]
|
|
31
|
+
console.log(blogs.meta); // { page, page_size, total_pages, total_items }
|
|
32
|
+
|
|
33
|
+
// By category
|
|
34
|
+
const techBlogs = await forja.blogs.listByCategory('tech', { page: 1 });
|
|
35
|
+
|
|
36
|
+
// Featured blogs
|
|
37
|
+
const featured = await forja.blogs.listFeatured({ limit: 5 });
|
|
38
|
+
|
|
39
|
+
// Similar posts
|
|
40
|
+
const similar = await forja.blogs.listSimilar('blog-uuid', { limit: 3 });
|
|
41
|
+
|
|
42
|
+
// Single blog (returns null if not found)
|
|
43
|
+
const blog = await forja.blogs.get('my-blog-slug');
|
|
44
|
+
|
|
45
|
+
// RSS feed
|
|
46
|
+
const rss = await forja.blogs.rss();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Pages
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Get page by route (returns null if not found)
|
|
53
|
+
const page = await forja.pages.getByRoute('/about');
|
|
54
|
+
|
|
55
|
+
// Page sections
|
|
56
|
+
const sections = await forja.pages.getSections('page-uuid');
|
|
57
|
+
|
|
58
|
+
// Section translations
|
|
59
|
+
const translations = await forja.pages.getSectionLocalizations('section-uuid');
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Navigation
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// All menus
|
|
66
|
+
const menus = await forja.navigation.listMenus();
|
|
67
|
+
|
|
68
|
+
// Single menu by slug
|
|
69
|
+
const primary = await forja.navigation.getMenuBySlug('primary');
|
|
70
|
+
|
|
71
|
+
// Navigation tree (with optional locale)
|
|
72
|
+
const tree = await forja.navigation.getTree('menu-uuid', { locale: 'de' });
|
|
73
|
+
|
|
74
|
+
// Menu items
|
|
75
|
+
const items = await forja.navigation.listItems('menu-uuid');
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Taxonomy
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Tags (paginated, searchable)
|
|
82
|
+
const tags = await forja.taxonomy.listTags({ search: 'rust', sortBy: 'slug' });
|
|
83
|
+
|
|
84
|
+
// Categories
|
|
85
|
+
const categories = await forja.taxonomy.listCategories();
|
|
86
|
+
|
|
87
|
+
// Categories with blog counts
|
|
88
|
+
const withCounts = await forja.taxonomy.getCategoriesWithBlogCounts();
|
|
89
|
+
|
|
90
|
+
// Tags/categories for a specific content item
|
|
91
|
+
const contentTags = await forja.taxonomy.getContentTags('content-uuid');
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Analytics
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Track a pageview
|
|
98
|
+
await forja.analytics.trackPageview({ path: '/blog/hello-world' });
|
|
99
|
+
|
|
100
|
+
// Analytics report
|
|
101
|
+
const report = await forja.analytics.getReport({ days: 30, topN: 10 });
|
|
102
|
+
console.log(report.total_views, report.total_unique_visitors);
|
|
103
|
+
|
|
104
|
+
// Page-specific analytics
|
|
105
|
+
const pageStats = await forja.analytics.getPageAnalytics({
|
|
106
|
+
path: '/blog/hello-world',
|
|
107
|
+
days: 7,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### CV / Resume
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Skills
|
|
115
|
+
const skills = await forja.cv.listSkills({ page: 1 });
|
|
116
|
+
const skill = await forja.cv.getSkillBySlug('typescript');
|
|
117
|
+
|
|
118
|
+
// CV entries filtered by type
|
|
119
|
+
const workHistory = await forja.cv.listEntries({ entryType: 'Work' });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Legal
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Legal documents
|
|
126
|
+
const docs = await forja.legal.list();
|
|
127
|
+
const privacy = await forja.legal.getBySlug('privacy-policy');
|
|
128
|
+
|
|
129
|
+
// Cookie consent
|
|
130
|
+
const consent = await forja.legal.getCookieConsent();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Pagination
|
|
134
|
+
|
|
135
|
+
All paginated responses include helpers for navigating pages:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const page1 = await forja.blogs.listPublished({ pageSize: 10 });
|
|
139
|
+
|
|
140
|
+
// Fetch next page
|
|
141
|
+
const page2 = await page1.fetchNext(); // null if on last page
|
|
142
|
+
|
|
143
|
+
// Fetch all items across all pages
|
|
144
|
+
const allBlogs = await page1.fetchAll();
|
|
145
|
+
|
|
146
|
+
// Async iterator
|
|
147
|
+
for await (const page of page1) {
|
|
148
|
+
console.log(page.data);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Error Handling
|
|
153
|
+
|
|
154
|
+
The SDK throws typed errors for different failure scenarios:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import {
|
|
158
|
+
ForjaAuthError,
|
|
159
|
+
ForjaPermissionError,
|
|
160
|
+
ForjaRateLimitError,
|
|
161
|
+
ForjaValidationError,
|
|
162
|
+
ForjaServerError,
|
|
163
|
+
ForjaNetworkError,
|
|
164
|
+
} from '@forjacms/client';
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const blogs = await forja.blogs.listPublished();
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (error instanceof ForjaAuthError) {
|
|
170
|
+
// Invalid or missing API key (401)
|
|
171
|
+
} else if (error instanceof ForjaRateLimitError) {
|
|
172
|
+
// Rate limited (429) — check error.retryAfter
|
|
173
|
+
console.log(`Retry after ${error.retryAfter} seconds`);
|
|
174
|
+
} else if (error instanceof ForjaNetworkError) {
|
|
175
|
+
// Network failure — server unreachable
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Methods that fetch a single resource by ID or slug return `null` instead of throwing on 404.
|
|
181
|
+
|
|
182
|
+
## Custom Fetch
|
|
183
|
+
|
|
184
|
+
Pass a custom `fetch` implementation for edge runtimes or testing:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const forja = new ForjaClient({
|
|
188
|
+
baseUrl: 'https://cms.example.com/api/v1',
|
|
189
|
+
apiKey: 'dk_read_...',
|
|
190
|
+
siteId: 'your-site-uuid',
|
|
191
|
+
fetch: customFetchFn,
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Framework Integration
|
|
196
|
+
|
|
197
|
+
### React
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { ForjaClient } from '@forjacms/client';
|
|
201
|
+
import { useEffect, useState } from 'react';
|
|
202
|
+
|
|
203
|
+
const forja = new ForjaClient({ baseUrl: '...', apiKey: '...', siteId: '...' });
|
|
204
|
+
|
|
205
|
+
function BlogList() {
|
|
206
|
+
const [blogs, setBlogs] = useState([]);
|
|
207
|
+
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
forja.blogs.listPublished({ page: 1 }).then((res) => setBlogs(res.data));
|
|
210
|
+
}, []);
|
|
211
|
+
|
|
212
|
+
return blogs.map((b) => <article key={b.id}>{b.slug}</article>);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Angular (v17+)
|
|
217
|
+
|
|
218
|
+
The `@forjacms/client/angular` subpath provides Angular DI integration and a signal-based resource helper.
|
|
219
|
+
|
|
220
|
+
**Setup:**
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// app.config.ts
|
|
224
|
+
import { provideForja } from '@forjacms/client/angular';
|
|
225
|
+
|
|
226
|
+
export const appConfig: ApplicationConfig = {
|
|
227
|
+
providers: [
|
|
228
|
+
provideForja({
|
|
229
|
+
baseUrl: environment.cmsApiUrl,
|
|
230
|
+
apiKey: environment.cmsApiKey,
|
|
231
|
+
siteId: environment.cmsSiteId,
|
|
232
|
+
}),
|
|
233
|
+
],
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Usage in components:**
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { Component } from '@angular/core';
|
|
241
|
+
import { injectForja, forjaResource } from '@forjacms/client/angular';
|
|
242
|
+
|
|
243
|
+
@Component({
|
|
244
|
+
template: `
|
|
245
|
+
@if (blogs.isLoading()) {
|
|
246
|
+
<p>Loading...</p>
|
|
247
|
+
} @else if (blogs.error()) {
|
|
248
|
+
<p>Error: {{ blogs.error()!.message }}</p>
|
|
249
|
+
} @else {
|
|
250
|
+
@for (blog of blogs.value()!.data; track blog.id) {
|
|
251
|
+
<article>{{ blog.slug }}</article>
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
<button (click)="blogs.reload()">Refresh</button>
|
|
255
|
+
`,
|
|
256
|
+
})
|
|
257
|
+
export class BlogListComponent {
|
|
258
|
+
private forja = injectForja();
|
|
259
|
+
blogs = forjaResource(() => this.forja.blogs.listPublished({ page: 1 }));
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The `forjaResource()` helper returns an object with:
|
|
264
|
+
- `value()` — Signal with the resolved data (or `undefined` while loading)
|
|
265
|
+
- `isLoading()` — Signal indicating loading state
|
|
266
|
+
- `error()` — Signal with the error (or `null` on success)
|
|
267
|
+
- `reload()` — Re-execute the loader
|
|
268
|
+
|
|
269
|
+
### Vanilla TypeScript
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { ForjaClient } from '@forjacms/client';
|
|
273
|
+
|
|
274
|
+
const forja = new ForjaClient({ baseUrl: '...', apiKey: '...', siteId: '...' });
|
|
275
|
+
|
|
276
|
+
const blogs = await forja.blogs.listPublished();
|
|
277
|
+
document.getElementById('count')!.textContent = `${blogs.meta.total_items} posts`;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
AGPL-3.0-or-later
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../client-eFMh_n88.cjs`);let t=require(`@angular/core`);const n=new t.InjectionToken(`ForjaClient`);function r(r){return(0,t.makeEnvironmentProviders)([{provide:n,useFactory:()=>new e.t(r)}])}function i(){return(0,t.inject)(n)}function a(e){let n=(0,t.signal)(void 0),r=(0,t.signal)(!0),i=(0,t.signal)(null),a=()=>{r.set(!0),i.set(null),e().then(e=>n.set(e)).catch(e=>i.set(e instanceof Error?e:Error(String(e)))).finally(()=>r.set(!1))};return a(),{value:n.asReadonly(),isLoading:r.asReadonly(),error:i.asReadonly(),reload:a}}exports.FORJA_CLIENT=n,exports.forjaResource=a,exports.injectForja=i,exports.provideForja=r;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { b as ForjaClientConfig, t as ForjaClient } from "../client-BBAKLM2r.cjs";
|
|
2
|
+
import { EnvironmentProviders, InjectionToken, Signal } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
//#region src/angular/provider.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Angular injection token for the {@link ForjaClient} instance.
|
|
7
|
+
*
|
|
8
|
+
* You don't need to use this directly — use {@link provideForja} and
|
|
9
|
+
* {@link injectForja} instead.
|
|
10
|
+
*/
|
|
11
|
+
declare const FORJA_CLIENT: InjectionToken<ForjaClient>;
|
|
12
|
+
/**
|
|
13
|
+
* Provide the Forja SDK client to Angular's dependency injection system.
|
|
14
|
+
*
|
|
15
|
+
* Call this in your application's `providers` array (typically in `app.config.ts`)
|
|
16
|
+
* to make the {@link ForjaClient} available via {@link injectForja}.
|
|
17
|
+
*
|
|
18
|
+
* @param config - API connection settings.
|
|
19
|
+
* @param config.baseUrl - Full URL to the Forja API (e.g. `https://cms.example.com/api/v1`).
|
|
20
|
+
* @param config.apiKey - API key with at least `Read` permission.
|
|
21
|
+
* @param config.siteId - UUID of the site to query.
|
|
22
|
+
* @returns Angular environment providers to include in your app config.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // app.config.ts
|
|
27
|
+
* import { provideForja } from '@forjacms/client/angular';
|
|
28
|
+
*
|
|
29
|
+
* export const appConfig: ApplicationConfig = {
|
|
30
|
+
* providers: [
|
|
31
|
+
* provideForja({
|
|
32
|
+
* baseUrl: environment.cmsApiUrl,
|
|
33
|
+
* apiKey: environment.cmsApiKey,
|
|
34
|
+
* siteId: environment.cmsSiteId,
|
|
35
|
+
* }),
|
|
36
|
+
* ],
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function provideForja(config: ForjaClientConfig): EnvironmentProviders;
|
|
41
|
+
/**
|
|
42
|
+
* Inject the {@link ForjaClient} instance from Angular's DI system.
|
|
43
|
+
*
|
|
44
|
+
* Must be called in an injection context (component constructor, `inject()` call,
|
|
45
|
+
* or factory function). Requires {@link provideForja} to be configured in the
|
|
46
|
+
* application's providers.
|
|
47
|
+
*
|
|
48
|
+
* @returns The configured {@link ForjaClient} instance.
|
|
49
|
+
* @throws If called outside an injection context or without {@link provideForja}.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { Component } from '@angular/core';
|
|
54
|
+
* import { injectForja, forjaResource } from '@forjacms/client/angular';
|
|
55
|
+
*
|
|
56
|
+
* @Component({
|
|
57
|
+
* template: `
|
|
58
|
+
* @if (blogs.isLoading()) { <p>Loading...</p> }
|
|
59
|
+
* @for (blog of blogs.value()?.data ?? []; track blog.id) {
|
|
60
|
+
* <p>{{ blog.slug }}</p>
|
|
61
|
+
* }
|
|
62
|
+
* `,
|
|
63
|
+
* })
|
|
64
|
+
* export class BlogListComponent {
|
|
65
|
+
* private forja = injectForja();
|
|
66
|
+
* blogs = forjaResource(() => this.forja.blogs.listPublished({ page: 1 }));
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function injectForja(): ForjaClient;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/angular/resource.d.ts
|
|
73
|
+
/**
|
|
74
|
+
* A reactive resource that wraps an async operation with Angular signals.
|
|
75
|
+
*
|
|
76
|
+
* Provides `value`, `isLoading`, and `error` signals for template binding,
|
|
77
|
+
* plus a `reload()` method to re-execute the loader.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam T - The type of the resolved value.
|
|
80
|
+
*/
|
|
81
|
+
interface ForjaResource<T> {
|
|
82
|
+
/** The resolved value, or `undefined` while loading or after an error. */
|
|
83
|
+
readonly value: Signal<T | undefined>;
|
|
84
|
+
/** `true` while the loader is executing. */
|
|
85
|
+
readonly isLoading: Signal<boolean>;
|
|
86
|
+
/** The error if the loader rejected, or `null` on success. */
|
|
87
|
+
readonly error: Signal<Error | null>;
|
|
88
|
+
/** Re-execute the loader, resetting `isLoading` and clearing `error`. */
|
|
89
|
+
readonly reload: () => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a signal-based resource from an async loader function.
|
|
93
|
+
*
|
|
94
|
+
* Immediately invokes the loader and exposes the result via Angular signals.
|
|
95
|
+
* Use in Angular components to bind async SDK calls to templates without
|
|
96
|
+
* manual subscribe/unsubscribe boilerplate.
|
|
97
|
+
*
|
|
98
|
+
* @typeParam T - The type returned by the loader promise.
|
|
99
|
+
* @param loader - A function that returns a `Promise<T>`. Called immediately and on each `reload()`.
|
|
100
|
+
* @returns A {@link ForjaResource} with reactive `value`, `isLoading`, and `error` signals.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { Component } from '@angular/core';
|
|
105
|
+
* import { injectForja, forjaResource } from '@forjacms/client/angular';
|
|
106
|
+
*
|
|
107
|
+
* @Component({
|
|
108
|
+
* template: `
|
|
109
|
+
* @if (blogs.isLoading()) {
|
|
110
|
+
* <p>Loading...</p>
|
|
111
|
+
* } @else if (blogs.error()) {
|
|
112
|
+
* <p>Error: {{ blogs.error()!.message }}</p>
|
|
113
|
+
* } @else {
|
|
114
|
+
* @for (blog of blogs.value()!.data; track blog.id) {
|
|
115
|
+
* <article>{{ blog.slug }}</article>
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* <button (click)="blogs.reload()">Refresh</button>
|
|
119
|
+
* `,
|
|
120
|
+
* })
|
|
121
|
+
* export class BlogListComponent {
|
|
122
|
+
* private forja = injectForja();
|
|
123
|
+
* blogs = forjaResource(() => this.forja.blogs.listPublished({ page: 1 }));
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* // Fetch a single resource
|
|
130
|
+
* const blog = forjaResource(() => forja.blogs.getBySlug('my-post'));
|
|
131
|
+
*
|
|
132
|
+
* // Access in template
|
|
133
|
+
* // blog.value()?.localizations[0]?.title
|
|
134
|
+
* // blog.isLoading()
|
|
135
|
+
* // blog.error()?.message
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare function forjaResource<T>(loader: () => Promise<T>): ForjaResource<T>;
|
|
139
|
+
//#endregion
|
|
140
|
+
export { FORJA_CLIENT, type ForjaResource, forjaResource, injectForja, provideForja };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { b as ForjaClientConfig, t as ForjaClient } from "../client-d-QwwKUW.mjs";
|
|
2
|
+
import { EnvironmentProviders, InjectionToken, Signal } from "@angular/core";
|
|
3
|
+
|
|
4
|
+
//#region src/angular/provider.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Angular injection token for the {@link ForjaClient} instance.
|
|
7
|
+
*
|
|
8
|
+
* You don't need to use this directly — use {@link provideForja} and
|
|
9
|
+
* {@link injectForja} instead.
|
|
10
|
+
*/
|
|
11
|
+
declare const FORJA_CLIENT: InjectionToken<ForjaClient>;
|
|
12
|
+
/**
|
|
13
|
+
* Provide the Forja SDK client to Angular's dependency injection system.
|
|
14
|
+
*
|
|
15
|
+
* Call this in your application's `providers` array (typically in `app.config.ts`)
|
|
16
|
+
* to make the {@link ForjaClient} available via {@link injectForja}.
|
|
17
|
+
*
|
|
18
|
+
* @param config - API connection settings.
|
|
19
|
+
* @param config.baseUrl - Full URL to the Forja API (e.g. `https://cms.example.com/api/v1`).
|
|
20
|
+
* @param config.apiKey - API key with at least `Read` permission.
|
|
21
|
+
* @param config.siteId - UUID of the site to query.
|
|
22
|
+
* @returns Angular environment providers to include in your app config.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // app.config.ts
|
|
27
|
+
* import { provideForja } from '@forjacms/client/angular';
|
|
28
|
+
*
|
|
29
|
+
* export const appConfig: ApplicationConfig = {
|
|
30
|
+
* providers: [
|
|
31
|
+
* provideForja({
|
|
32
|
+
* baseUrl: environment.cmsApiUrl,
|
|
33
|
+
* apiKey: environment.cmsApiKey,
|
|
34
|
+
* siteId: environment.cmsSiteId,
|
|
35
|
+
* }),
|
|
36
|
+
* ],
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function provideForja(config: ForjaClientConfig): EnvironmentProviders;
|
|
41
|
+
/**
|
|
42
|
+
* Inject the {@link ForjaClient} instance from Angular's DI system.
|
|
43
|
+
*
|
|
44
|
+
* Must be called in an injection context (component constructor, `inject()` call,
|
|
45
|
+
* or factory function). Requires {@link provideForja} to be configured in the
|
|
46
|
+
* application's providers.
|
|
47
|
+
*
|
|
48
|
+
* @returns The configured {@link ForjaClient} instance.
|
|
49
|
+
* @throws If called outside an injection context or without {@link provideForja}.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { Component } from '@angular/core';
|
|
54
|
+
* import { injectForja, forjaResource } from '@forjacms/client/angular';
|
|
55
|
+
*
|
|
56
|
+
* @Component({
|
|
57
|
+
* template: `
|
|
58
|
+
* @if (blogs.isLoading()) { <p>Loading...</p> }
|
|
59
|
+
* @for (blog of blogs.value()?.data ?? []; track blog.id) {
|
|
60
|
+
* <p>{{ blog.slug }}</p>
|
|
61
|
+
* }
|
|
62
|
+
* `,
|
|
63
|
+
* })
|
|
64
|
+
* export class BlogListComponent {
|
|
65
|
+
* private forja = injectForja();
|
|
66
|
+
* blogs = forjaResource(() => this.forja.blogs.listPublished({ page: 1 }));
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function injectForja(): ForjaClient;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/angular/resource.d.ts
|
|
73
|
+
/**
|
|
74
|
+
* A reactive resource that wraps an async operation with Angular signals.
|
|
75
|
+
*
|
|
76
|
+
* Provides `value`, `isLoading`, and `error` signals for template binding,
|
|
77
|
+
* plus a `reload()` method to re-execute the loader.
|
|
78
|
+
*
|
|
79
|
+
* @typeParam T - The type of the resolved value.
|
|
80
|
+
*/
|
|
81
|
+
interface ForjaResource<T> {
|
|
82
|
+
/** The resolved value, or `undefined` while loading or after an error. */
|
|
83
|
+
readonly value: Signal<T | undefined>;
|
|
84
|
+
/** `true` while the loader is executing. */
|
|
85
|
+
readonly isLoading: Signal<boolean>;
|
|
86
|
+
/** The error if the loader rejected, or `null` on success. */
|
|
87
|
+
readonly error: Signal<Error | null>;
|
|
88
|
+
/** Re-execute the loader, resetting `isLoading` and clearing `error`. */
|
|
89
|
+
readonly reload: () => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a signal-based resource from an async loader function.
|
|
93
|
+
*
|
|
94
|
+
* Immediately invokes the loader and exposes the result via Angular signals.
|
|
95
|
+
* Use in Angular components to bind async SDK calls to templates without
|
|
96
|
+
* manual subscribe/unsubscribe boilerplate.
|
|
97
|
+
*
|
|
98
|
+
* @typeParam T - The type returned by the loader promise.
|
|
99
|
+
* @param loader - A function that returns a `Promise<T>`. Called immediately and on each `reload()`.
|
|
100
|
+
* @returns A {@link ForjaResource} with reactive `value`, `isLoading`, and `error` signals.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { Component } from '@angular/core';
|
|
105
|
+
* import { injectForja, forjaResource } from '@forjacms/client/angular';
|
|
106
|
+
*
|
|
107
|
+
* @Component({
|
|
108
|
+
* template: `
|
|
109
|
+
* @if (blogs.isLoading()) {
|
|
110
|
+
* <p>Loading...</p>
|
|
111
|
+
* } @else if (blogs.error()) {
|
|
112
|
+
* <p>Error: {{ blogs.error()!.message }}</p>
|
|
113
|
+
* } @else {
|
|
114
|
+
* @for (blog of blogs.value()!.data; track blog.id) {
|
|
115
|
+
* <article>{{ blog.slug }}</article>
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* <button (click)="blogs.reload()">Refresh</button>
|
|
119
|
+
* `,
|
|
120
|
+
* })
|
|
121
|
+
* export class BlogListComponent {
|
|
122
|
+
* private forja = injectForja();
|
|
123
|
+
* blogs = forjaResource(() => this.forja.blogs.listPublished({ page: 1 }));
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* // Fetch a single resource
|
|
130
|
+
* const blog = forjaResource(() => forja.blogs.getBySlug('my-post'));
|
|
131
|
+
*
|
|
132
|
+
* // Access in template
|
|
133
|
+
* // blog.value()?.localizations[0]?.title
|
|
134
|
+
* // blog.isLoading()
|
|
135
|
+
* // blog.error()?.message
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare function forjaResource<T>(loader: () => Promise<T>): ForjaResource<T>;
|
|
139
|
+
//#endregion
|
|
140
|
+
export { FORJA_CLIENT, type ForjaResource, forjaResource, injectForja, provideForja };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"../client-DTfhjUoX.mjs";import{InjectionToken as t,inject as n,makeEnvironmentProviders as r,signal as i}from"@angular/core";const a=new t(`ForjaClient`);function o(t){return r([{provide:a,useFactory:()=>new e(t)}])}function s(){return n(a)}function c(e){let t=i(void 0),n=i(!0),r=i(null),a=()=>{n.set(!0),r.set(null),e().then(e=>t.set(e)).catch(e=>r.set(e instanceof Error?e:Error(String(e)))).finally(()=>n.set(!1))};return a(),{value:t.asReadonly(),isLoading:n.asReadonly(),error:r.asReadonly(),reload:a}}export{a as FORJA_CLIENT,c as forjaResource,s as injectForja,o as provideForja};
|