@content-streamline/nextjs 0.0.3 → 0.0.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.
@@ -6,22 +6,40 @@ import { createAPIClient } from '../api/client.js';
6
6
  import { getAllCachedPosts, getCachedPost, cachePost, updatePostsIndex, shouldRefreshCache, } from '../cache/file-cache.js';
7
7
  /**
8
8
  * Get API configuration from environment variables
9
+ * Returns null if not configured (instead of throwing)
9
10
  */
10
11
  function getAPIConfig() {
11
12
  const baseUrl = process.env.CONTENT_STREAMLINE_API_BASE_URL || process.env.API_BASE_URL;
12
13
  const accessToken = process.env.CONTENT_STREAMLINE_API_ACCESS_TOKEN || process.env.API_ACCESS_TOKEN;
13
14
  if (!baseUrl || !accessToken) {
14
- throw new Error('Missing API configuration. Set CONTENT_STREAMLINE_API_BASE_URL and CONTENT_STREAMLINE_API_ACCESS_TOKEN environment variables.');
15
+ return null;
15
16
  }
16
17
  return { baseUrl, accessToken };
17
18
  }
19
+ /**
20
+ * Get API configuration, throwing if not configured
21
+ * Use this when API is required
22
+ */
23
+ function requireAPIConfig() {
24
+ const config = getAPIConfig();
25
+ if (!config) {
26
+ throw new Error('Missing API configuration. Set CONTENT_STREAMLINE_API_BASE_URL and CONTENT_STREAMLINE_API_ACCESS_TOKEN environment variables.');
27
+ }
28
+ return config;
29
+ }
18
30
  /**
19
31
  * Get all posts (with automatic cache refresh)
20
32
  * Use this in Server Components or API Routes
33
+ *
34
+ * Note: Only successful API responses are cached. Failed API calls
35
+ * (network errors, auth failures, etc.) will NOT be cached, allowing
36
+ * subsequent calls to retry the API.
21
37
  */
22
38
  export async function getAllPosts(options) {
23
- const { maxCacheAge = 60, forceRefresh = false, platform } = options || {};
24
- // Check cache freshness
39
+ const { maxCacheAge = 15, forceRefresh = false, platform } = options || {};
40
+ // Check if API is configured
41
+ const apiConfig = getAPIConfig();
42
+ // Check cache freshness (this also validates cache metadata)
25
43
  if (!forceRefresh) {
26
44
  const needsRefresh = await shouldRefreshCache(maxCacheAge);
27
45
  if (!needsRefresh) {
@@ -40,13 +58,36 @@ export async function getAllPosts(options) {
40
58
  }
41
59
  }
42
60
  }
61
+ // If API is not configured, return empty array but DON'T cache it
62
+ if (!apiConfig) {
63
+ console.warn('Content Streamline: API not configured. Set CONTENT_STREAMLINE_API_BASE_URL and CONTENT_STREAMLINE_API_ACCESS_TOKEN environment variables.');
64
+ return [];
65
+ }
43
66
  // Fetch from API
44
- const { baseUrl, accessToken } = getAPIConfig();
45
- const api = createAPIClient(baseUrl, accessToken);
46
- const posts = await api.getAllPosts(platform);
47
- // Cache all posts
67
+ let posts;
68
+ try {
69
+ const api = createAPIClient(apiConfig.baseUrl, apiConfig.accessToken);
70
+ posts = await api.getAllPosts(platform);
71
+ }
72
+ catch (error) {
73
+ // API call failed - DON'T cache the error, just return empty
74
+ // This allows subsequent calls to retry the API
75
+ console.error('Content Streamline: Failed to fetch posts from API:', error);
76
+ // Try to return stale cache if available (better than nothing)
77
+ const staleCached = await getAllCachedPosts();
78
+ if (staleCached.length > 0) {
79
+ console.warn('Content Streamline: Returning stale cached data due to API error');
80
+ if (platform) {
81
+ return staleCached.filter(post => post.platform.toLowerCase() === platform.toLowerCase());
82
+ }
83
+ return staleCached;
84
+ }
85
+ return [];
86
+ }
87
+ // API call succeeded - cache the results with success metadata
48
88
  for (const post of posts) {
49
89
  try {
90
+ const api = createAPIClient(apiConfig.baseUrl, apiConfig.accessToken);
50
91
  const fullPost = await api.getPost(post.id, 'markdown');
51
92
  await cachePost(fullPost);
52
93
  }
@@ -54,17 +95,22 @@ export async function getAllPosts(options) {
54
95
  console.error(`Failed to cache post ${post.id}:`, error);
55
96
  }
56
97
  }
57
- // Update index
58
- await updatePostsIndex(posts);
98
+ // Update index with success metadata
99
+ await updatePostsIndex(posts, true);
59
100
  return posts;
60
101
  }
61
102
  /**
62
103
  * Get a single post by ID
63
104
  * Use this in Server Components or API Routes
105
+ *
106
+ * Note: Only successful API responses are cached. Failed API calls
107
+ * will NOT be cached, allowing subsequent calls to retry the API.
64
108
  */
65
109
  export async function getPost(id, options) {
66
- const { language = 'en', contentFormat = 'markdown', maxCacheAge = 60, forceRefresh = false, } = options || {};
67
- // Try cache first
110
+ const { language = 'en', contentFormat = 'markdown', maxCacheAge = 15, forceRefresh = false, } = options || {};
111
+ // Check if API is configured
112
+ const apiConfig = getAPIConfig();
113
+ // Try cache first (cache validation includes metadata checks)
68
114
  if (!forceRefresh) {
69
115
  const needsRefresh = await shouldRefreshCache(maxCacheAge);
70
116
  if (!needsRefresh) {
@@ -74,17 +120,28 @@ export async function getPost(id, options) {
74
120
  }
75
121
  }
76
122
  }
123
+ // If API is not configured, return null but DON'T cache it
124
+ if (!apiConfig) {
125
+ console.warn('Content Streamline: API not configured. Set CONTENT_STREAMLINE_API_BASE_URL and CONTENT_STREAMLINE_API_ACCESS_TOKEN environment variables.');
126
+ return null;
127
+ }
77
128
  // Fetch from API
78
129
  try {
79
- const { baseUrl, accessToken } = getAPIConfig();
80
- const api = createAPIClient(baseUrl, accessToken);
130
+ const api = createAPIClient(apiConfig.baseUrl, apiConfig.accessToken);
81
131
  const post = await api.getPost(id, contentFormat);
82
- // Cache it
132
+ // API call succeeded - cache it
83
133
  await cachePost(post);
84
134
  return post;
85
135
  }
86
136
  catch (error) {
87
- console.error(`Failed to fetch post ${id}:`, error);
137
+ // API call failed - DON'T cache the error
138
+ // Try to return stale cache if available
139
+ console.error(`Content Streamline: Failed to fetch post ${id}:`, error);
140
+ const staleCached = await getCachedPost(id, language);
141
+ if (staleCached) {
142
+ console.warn(`Content Streamline: Returning stale cached data for post ${id} due to API error`);
143
+ return staleCached;
144
+ }
88
145
  return null;
89
146
  }
90
147
  }
@@ -39,6 +39,15 @@ export interface PostsResponse {
39
39
  export interface PostDetailResponse extends Post {
40
40
  post_translations: PostTranslation[];
41
41
  }
42
+ /**
43
+ * Metadata stored with cache entries to track API state
44
+ */
45
+ export interface CacheMetadata {
46
+ timestamp: number;
47
+ apiConfigured: boolean;
48
+ apiCallSucceeded: boolean;
49
+ envConfigHash?: string;
50
+ }
42
51
  export interface CachedPostIndex {
43
52
  posts: Array<{
44
53
  id: number;
@@ -47,5 +56,6 @@ export interface CachedPostIndex {
47
56
  updated_at: string;
48
57
  }>;
49
58
  last_sync: string;
59
+ metadata?: CacheMetadata;
50
60
  }
51
61
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,IAAI,EAAE,CAAC;CACd;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI;IAC9C,iBAAiB,EAAE,eAAe,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,IAAI,EAAE,CAAC;CACd;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI;IAC9C,iBAAiB,EAAE,eAAe,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@content-streamline/nextjs",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Next.js library for Content Streamline API with reusable components",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",