naystack 1.9.0 → 1.9.2

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 CHANGED
@@ -713,7 +713,8 @@ constant so the generalized pieces pick it up for free.
713
713
  A provider is a plain object with no per-instance state — import the constant, no
714
714
  construction. Capability is method presence: a platform that can't refresh tokens
715
715
  omits `refresh`, one with no content-listing API omits `fetchMedia`, and callers
716
- narrow with a plain `if`.
716
+ narrow with a plain `if`. Platform *knowledge* is not a capability, so it is
717
+ always there: `platform` and `profileURL(username)` are required of every adapter.
717
718
 
718
719
  ```typescript
719
720
  import { InstagramProvider } from "naystack/socials";
@@ -723,6 +724,12 @@ const profile = await InstagramProvider.fetchProfile(accessToken);
723
724
 
724
725
  const posts = await InstagramProvider.fetchMedia?.(accessToken, { limit: 6 });
725
726
  // [{ kind: "video", permalink: "…", likes: 812, comments: null, … }]
727
+ // null instead means the request failed — an account with nothing posted
728
+ // returns []. Cache the empty, retry the null.
729
+
730
+ InstagramProvider.profileURL(profile.username);
731
+ // "https://instagram.com/…" — so a stored account row links out without the
732
+ // consumer keeping its own per-platform URL table
726
733
  ```
727
734
 
728
735
  Every metric on a `SocialPost` is `number | null`, and `null` always means *the
@@ -444,6 +444,7 @@ var InstagramProvider = {
444
444
  };
445
445
  }
446
446
  },
447
+ profileURL: (username) => `https://instagram.com/${username}`,
447
448
  fetchProfile: async (accessToken) => {
448
449
  const user = await getInstagramUser(
449
450
  accessToken,
@@ -470,7 +471,11 @@ var InstagramProvider = {
470
471
  MEDIA_FIELDS,
471
472
  options?.limit
472
473
  );
473
- if (!result?.data) return [];
474
+ if (!Array.isArray(result?.data)) {
475
+ if (result?.error)
476
+ console.error("[naystack] Instagram media:", result.error.message);
477
+ return null;
478
+ }
474
479
  return result.data.map(toPost);
475
480
  }
476
481
  };
@@ -388,6 +388,7 @@ var InstagramProvider = {
388
388
  };
389
389
  }
390
390
  },
391
+ profileURL: (username) => `https://instagram.com/${username}`,
391
392
  fetchProfile: async (accessToken) => {
392
393
  const user = await getInstagramUser(
393
394
  accessToken,
@@ -414,7 +415,11 @@ var InstagramProvider = {
414
415
  MEDIA_FIELDS,
415
416
  options?.limit
416
417
  );
417
- if (!result?.data) return [];
418
+ if (!Array.isArray(result?.data)) {
419
+ if (result?.error)
420
+ console.error("[naystack] Instagram media:", result.error.message);
421
+ return null;
422
+ }
418
423
  return result.data.map(toPost);
419
424
  }
420
425
  };
@@ -239,6 +239,7 @@ var InstagramProvider = {
239
239
  };
240
240
  }
241
241
  },
242
+ profileURL: (username) => `https://instagram.com/${username}`,
242
243
  fetchProfile: async (accessToken) => {
243
244
  const user = await getInstagramUser(
244
245
  accessToken,
@@ -265,7 +266,11 @@ var InstagramProvider = {
265
266
  MEDIA_FIELDS,
266
267
  options?.limit
267
268
  );
268
- if (!result?.data) return [];
269
+ if (!Array.isArray(result?.data)) {
270
+ if (result?.error)
271
+ console.error("[naystack] Instagram media:", result.error.message);
272
+ return null;
273
+ }
269
274
  return result.data.map(toPost);
270
275
  }
271
276
  };
@@ -213,6 +213,7 @@ var InstagramProvider = {
213
213
  };
214
214
  }
215
215
  },
216
+ profileURL: (username) => `https://instagram.com/${username}`,
216
217
  fetchProfile: async (accessToken) => {
217
218
  const user = await getInstagramUser(
218
219
  accessToken,
@@ -239,7 +240,11 @@ var InstagramProvider = {
239
240
  MEDIA_FIELDS,
240
241
  options?.limit
241
242
  );
242
- if (!result?.data) return [];
243
+ if (!Array.isArray(result?.data)) {
244
+ if (result?.error)
245
+ console.error("[naystack] Instagram media:", result.error.message);
246
+ return null;
247
+ }
243
248
  return result.data.map(toPost);
244
249
  }
245
250
  };
@@ -15,7 +15,8 @@ import { SocialPlatform, SocialTokens, SocialProfile, SocialPost } from './types
15
15
  * @property auth.authorizationURL - `state` comes back on the callback; `redirectURI` must match what the platform has registered.
16
16
  * @property auth.exchangeCode - Trades the callback `code` for tokens. `platformUserId` is `null` on platforms whose token response carries no id (Google); the route then takes it from `fetchProfile`.
17
17
  * @property auth.refresh - Omitted by platforms whose tokens don't expire or can't be refreshed.
18
- * @property fetchMedia - Omitted by platforms with no content-listing API.
18
+ * @property fetchMedia - Omitted by platforms with no content-listing API. Resolves to `null` when the request failed and `[]` when the account genuinely has no content — a caller that caches results must not treat an outage as an empty grid.
19
+ * @property profileURL - Public profile link for a handle. Platform knowledge, not a capability, so every adapter has one.
19
20
  *
20
21
  * @example
21
22
  * ```ts
@@ -44,9 +45,10 @@ type SocialProvider = {
44
45
  refresh?: (tokens: SocialTokens) => Promise<SocialTokens | null>;
45
46
  };
46
47
  fetchProfile: (accessToken: string) => Promise<SocialProfile | null>;
48
+ profileURL: (username: string) => string;
47
49
  fetchMedia?: (accessToken: string, options?: {
48
50
  limit?: number;
49
- }) => Promise<SocialPost[]>;
51
+ }) => Promise<SocialPost[] | null>;
50
52
  };
51
53
 
52
54
  export type { SocialProvider };
@@ -15,7 +15,8 @@ import { SocialPlatform, SocialTokens, SocialProfile, SocialPost } from './types
15
15
  * @property auth.authorizationURL - `state` comes back on the callback; `redirectURI` must match what the platform has registered.
16
16
  * @property auth.exchangeCode - Trades the callback `code` for tokens. `platformUserId` is `null` on platforms whose token response carries no id (Google); the route then takes it from `fetchProfile`.
17
17
  * @property auth.refresh - Omitted by platforms whose tokens don't expire or can't be refreshed.
18
- * @property fetchMedia - Omitted by platforms with no content-listing API.
18
+ * @property fetchMedia - Omitted by platforms with no content-listing API. Resolves to `null` when the request failed and `[]` when the account genuinely has no content — a caller that caches results must not treat an outage as an empty grid.
19
+ * @property profileURL - Public profile link for a handle. Platform knowledge, not a capability, so every adapter has one.
19
20
  *
20
21
  * @example
21
22
  * ```ts
@@ -44,9 +45,10 @@ type SocialProvider = {
44
45
  refresh?: (tokens: SocialTokens) => Promise<SocialTokens | null>;
45
46
  };
46
47
  fetchProfile: (accessToken: string) => Promise<SocialProfile | null>;
48
+ profileURL: (username: string) => string;
47
49
  fetchMedia?: (accessToken: string, options?: {
48
50
  limit?: number;
49
- }) => Promise<SocialPost[]>;
51
+ }) => Promise<SocialPost[] | null>;
50
52
  };
51
53
 
52
54
  export type { SocialProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naystack",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "A stack built with Next + GraphQL + S3 + Auth",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",