@content-streamline/nextjs 0.0.5 → 0.0.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EAClB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B,cAAM,oBAAoB;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;YAKlC,KAAK;IAiCnB;;OAEG;IACG,SAAS,CAAC,IAAI,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAS5E;;OAEG;IACG,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAgBrD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,GAAE,UAAU,GAAG,MAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAIxG;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAE1F"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EAClB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B,cAAM,oBAAoB;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;YAKlC,KAAK;IAsDnB;;OAEG;IACG,SAAS,CAAC,IAAI,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAS5E;;OAEG;IACG,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IA4BrD;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,GAAE,UAAU,GAAG,MAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAIxG;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAE1F"}
@@ -13,14 +13,31 @@ class ContentStreamlineAPI {
13
13
  'Content-Type': 'application/json',
14
14
  ...options.headers,
15
15
  };
16
- const response = await fetch(url, {
17
- ...options,
18
- headers,
19
- });
16
+ let response;
17
+ try {
18
+ response = await fetch(url, {
19
+ ...options,
20
+ headers,
21
+ });
22
+ }
23
+ catch (error) {
24
+ // Network error (connection failed, timeout, etc.)
25
+ const errorMessage = error instanceof Error ? error.message : String(error);
26
+ throw new Error(`Network error: ${errorMessage}`);
27
+ }
20
28
  if (!response.ok) {
21
- if (response.status === 401) {
29
+ let errorMessage = `API request failed: ${response.status} ${response.statusText}`;
30
+ try {
22
31
  const errorText = await response.text();
23
- if (errorText.includes('Missing access token')) {
32
+ if (errorText) {
33
+ errorMessage += ` - ${errorText}`;
34
+ }
35
+ }
36
+ catch {
37
+ // Ignore error reading response body
38
+ }
39
+ if (response.status === 401) {
40
+ if (errorMessage.includes('Missing access token')) {
24
41
  throw new Error('Missing access token');
25
42
  }
26
43
  throw new Error('Invalid access token');
@@ -31,9 +48,15 @@ class ContentStreamlineAPI {
31
48
  if (response.status === 404) {
32
49
  throw new Error('Post not found');
33
50
  }
34
- throw new Error(`API request failed: ${response.status} ${response.statusText}`);
51
+ throw new Error(errorMessage);
52
+ }
53
+ try {
54
+ const data = await response.json();
55
+ return data;
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Failed to parse API response as JSON: ${error instanceof Error ? error.message : String(error)}`);
35
59
  }
36
- return response.json();
37
60
  }
38
61
  /**
39
62
  * List all posts with pagination
@@ -55,9 +78,18 @@ class ContentStreamlineAPI {
55
78
  let hasMore = true;
56
79
  while (hasMore) {
57
80
  const response = await this.listPosts(currentPage, platform);
81
+ // Validate response structure
82
+ if (!response || !Array.isArray(response.data)) {
83
+ throw new Error(`Invalid API response: expected PostsResponse with data array, got ${typeof response}`);
84
+ }
58
85
  allPosts.push(...response.data);
59
86
  hasMore = response.pagination.next_page !== null;
60
87
  currentPage = response.pagination.next_page || currentPage + 1;
88
+ // Safety check to prevent infinite loops
89
+ if (currentPage > 1000) {
90
+ console.warn('Content Streamline: Stopping pagination at page 1000 to prevent infinite loop');
91
+ break;
92
+ }
61
93
  }
62
94
  return allPosts;
63
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-cache.d.ts","sourceRoot":"","sources":["../../src/cache/file-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAMlG;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAIzC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAIzC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,OAAO,GAAG,aAAa,CAO5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CA8BvG;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAO/C;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAa,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAS3G;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CA0BzD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,gBAAgB,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBrG;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAOrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,GAAE,MAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBrF;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAYhD"}
1
+ {"version":3,"file":"file-cache.d.ts","sourceRoot":"","sources":["../../src/cache/file-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAkBlG;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAIzC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAIzC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,OAAO,GAAG,aAAa,CAO5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CA8BvG;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAO/C;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB9E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAa,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAS3G;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CA0BzD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,gBAAgB,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBrG;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAOrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,GAAE,MAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBrF;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAYhD"}
@@ -4,8 +4,19 @@
4
4
  */
5
5
  import { writeFile, readFile, mkdir, readdir, unlink } from 'fs/promises';
6
6
  import { join } from 'path';
7
+ import { tmpdir } from 'os';
7
8
  import { createHash } from 'crypto';
8
- const CACHE_DIR = join(process.cwd(), '.next', 'content-streamline-cache');
9
+ function resolveCacheDir() {
10
+ const configured = process.env.CONTENT_STREAMLINE_CACHE_DIR?.trim();
11
+ if (configured)
12
+ return configured;
13
+ // Vercel serverless filesystem is read-only except for /tmp
14
+ if (process.env.VERCEL) {
15
+ return join(tmpdir(), 'content-streamline-cache');
16
+ }
17
+ return join(process.cwd(), '.next', 'content-streamline-cache');
18
+ }
19
+ const CACHE_DIR = resolveCacheDir();
9
20
  const POSTS_INDEX_FILE = join(CACHE_DIR, 'posts-index.json');
10
21
  const POSTS_DIR = join(CACHE_DIR, 'posts');
11
22
  /**
@@ -86,7 +97,12 @@ export async function cachePost(post) {
86
97
  for (const translation of post.post_translations) {
87
98
  const filename = `${post.id}-${translation.language}.json`;
88
99
  const filepath = join(POSTS_DIR, filename);
89
- await writeFile(filepath, JSON.stringify(post, null, 2), 'utf-8');
100
+ try {
101
+ await writeFile(filepath, JSON.stringify(post, null, 2), 'utf-8');
102
+ }
103
+ catch (error) {
104
+ console.warn(`Content Streamline: Failed to write cache file ${filepath}:`, error instanceof Error ? error.message : String(error));
105
+ }
90
106
  }
91
107
  }
92
108
  /**
@@ -144,7 +160,12 @@ export async function updatePostsIndex(posts, apiCallSucceeded = true) {
144
160
  last_sync: new Date().toISOString(),
145
161
  metadata: createCacheMetadata(apiCallSucceeded),
146
162
  };
147
- await writeFile(POSTS_INDEX_FILE, JSON.stringify(index, null, 2), 'utf-8');
163
+ try {
164
+ await writeFile(POSTS_INDEX_FILE, JSON.stringify(index, null, 2), 'utf-8');
165
+ }
166
+ catch (error) {
167
+ console.warn(`Content Streamline: Failed to write posts index cache file ${POSTS_INDEX_FILE}:`, error instanceof Error ? error.message : String(error));
168
+ }
148
169
  }
149
170
  /**
150
171
  * Get the posts index
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA+BlE;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CA2ElB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAoDpC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2BpC;AAGD,YAAY,EACV,IAAI,EACJ,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA+BlE;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAyGlB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAoDpC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2BpC;AAGD,YAAY,EACV,IAAI,EACJ,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,aAAa,EACb,UAAU,GACX,MAAM,mBAAmB,CAAC"}
@@ -67,36 +67,62 @@ export async function getAllPosts(options) {
67
67
  let posts;
68
68
  try {
69
69
  const api = createAPIClient(apiConfig.baseUrl, apiConfig.accessToken);
70
+ console.log(`Content Streamline: Fetching posts from API (forceRefresh: ${forceRefresh}, platform: ${platform || 'all'})`);
70
71
  posts = await api.getAllPosts(platform);
72
+ console.log(`Content Streamline: Successfully fetched ${posts.length} posts from API`);
71
73
  }
72
74
  catch (error) {
73
75
  // API call failed - DON'T cache the error, just return empty
74
76
  // This allows subsequent calls to retry the API
75
- console.error('Content Streamline: Failed to fetch posts from API:', error);
77
+ const errorMessage = error instanceof Error ? error.message : String(error);
78
+ console.error('Content Streamline: Failed to fetch posts from API:', errorMessage);
79
+ if (error instanceof Error && error.stack) {
80
+ console.error('Content Streamline: Error stack:', error.stack);
81
+ }
76
82
  // Try to return stale cache if available (better than nothing)
77
83
  const staleCached = await getAllCachedPosts();
78
84
  if (staleCached.length > 0) {
79
- console.warn('Content Streamline: Returning stale cached data due to API error');
85
+ console.warn(`Content Streamline: Returning ${staleCached.length} stale cached posts due to API error`);
80
86
  if (platform) {
81
- return staleCached.filter(post => post.platform.toLowerCase() === platform.toLowerCase());
87
+ const filtered = staleCached.filter(post => post.platform.toLowerCase() === platform.toLowerCase());
88
+ return filtered;
82
89
  }
83
90
  return staleCached;
84
91
  }
92
+ console.warn('Content Streamline: No cached data available, returning empty array');
93
+ return [];
94
+ }
95
+ // Validate that we got posts
96
+ if (!Array.isArray(posts)) {
97
+ console.error('Content Streamline: API returned invalid response (not an array):', typeof posts);
85
98
  return [];
86
99
  }
87
100
  // API call succeeded - cache the results with success metadata
101
+ // Note: We cache individual post details, but failures here don't prevent returning the posts
102
+ let cachedCount = 0;
88
103
  for (const post of posts) {
89
104
  try {
90
105
  const api = createAPIClient(apiConfig.baseUrl, apiConfig.accessToken);
91
106
  const fullPost = await api.getPost(post.id, 'markdown');
92
107
  await cachePost(fullPost);
108
+ cachedCount++;
93
109
  }
94
110
  catch (error) {
95
- console.error(`Failed to cache post ${post.id}:`, error);
111
+ // Log but don't fail - we still want to return the posts
112
+ console.warn(`Content Streamline: Failed to cache full details for post ${post.id}:`, error instanceof Error ? error.message : String(error));
96
113
  }
97
114
  }
115
+ if (cachedCount > 0) {
116
+ console.log(`Content Streamline: Cached ${cachedCount} of ${posts.length} posts`);
117
+ }
98
118
  // Update index with success metadata
99
- await updatePostsIndex(posts, true);
119
+ try {
120
+ await updatePostsIndex(posts, true);
121
+ }
122
+ catch (error) {
123
+ console.warn('Content Streamline: Failed to update posts index cache (non-fatal):', error instanceof Error ? error.message : String(error));
124
+ }
125
+ console.log(`Content Streamline: Returning ${posts.length} posts`);
100
126
  return posts;
101
127
  }
102
128
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@content-streamline/nextjs",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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",