@kookee/sdk 0.0.31 → 0.0.33

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
@@ -44,6 +44,43 @@ const kookee = new Kookee({
44
44
  });
45
45
  ```
46
46
 
47
+ ## User Identification
48
+
49
+ Set user identity once and it's automatically used across all feedback operations:
50
+
51
+ ```typescript
52
+ // Set user identity globally
53
+ kookee.identify({
54
+ externalId: 'user-456',
55
+ name: 'Jane Doe',
56
+ email: 'jane@example.com', // optional
57
+ avatarUrl: 'https://...', // optional
58
+ });
59
+
60
+ // All feedback methods now auto-use the identified user
61
+ await kookee.feedback.createPost({ title: 'Feature request', category: 'feature' });
62
+ await kookee.feedback.createComment(postId, { content: 'Great idea!' });
63
+ const myPosts = await kookee.feedback.listMyPosts();
64
+
65
+ // Check current user
66
+ const user = kookee.getUser(); // KookeeUser | null
67
+
68
+ // Clear identity on logout
69
+ kookee.reset();
70
+ ```
71
+
72
+ ### React usage
73
+
74
+ ```typescript
75
+ useEffect(() => {
76
+ if (user) {
77
+ kookee.identify({ externalId: user.id, name: user.name, email: user.email });
78
+ } else {
79
+ kookee.reset();
80
+ }
81
+ }, [user]);
82
+ ```
83
+
47
84
  ## Blog
48
85
 
49
86
  ```typescript
@@ -219,48 +256,39 @@ const contributors = await kookee.feedback.getTopContributors({ limit: 10 });
219
256
 
220
257
  ### Creating and managing feedback
221
258
 
222
- These operations require an `ExternalUser` to identify the author:
259
+ These operations require user identification — either globally via `kookee.identify()` or per-call via `externalUser`/`externalId`:
223
260
 
224
261
  ```typescript
225
- import type { ExternalUser } from '@kookee/sdk';
226
-
227
- const user: ExternalUser = {
228
- externalId: 'user-123', // your system's user ID
229
- name: 'Jane Doe',
230
- email: 'jane@example.com', // optional
231
- avatarUrl: 'https://...', // optional
232
- };
262
+ // With global identity (recommended see User Identification section above)
263
+ kookee.identify({ externalId: 'user-123', name: 'Jane Doe' });
233
264
 
234
- // Create a feedback post
235
265
  const newPost = await kookee.feedback.createPost({
236
266
  title: 'Add dark mode',
237
267
  description: 'It would be great to have a dark mode option.',
238
- category: 'feature', // optional: 'feature' | 'improvement' | 'bug' | 'other'
239
- externalUser: user,
268
+ category: 'feature',
240
269
  });
241
270
 
242
- // Add a comment to a post
243
271
  const comment = await kookee.feedback.createComment('post-id', {
244
272
  content: 'Great idea, I would love this too!',
245
- externalUser: user,
246
273
  });
247
274
 
248
- // List posts created by a specific user
249
- const myPosts = await kookee.feedback.listMyPosts({
250
- externalId: 'user-123',
251
- page: 1,
252
- limit: 10,
253
- status: 'open', // optional filter
254
- category: 'feature', // optional filter
255
- search: 'dark mode', // optional search
256
- sort: 'newest', // optional sort
257
- });
275
+ const myPosts = await kookee.feedback.listMyPosts();
276
+
277
+ await kookee.feedback.deletePost('post-id');
278
+ await kookee.feedback.deleteComment('comment-id');
279
+ ```
280
+
281
+ Per-call override still works (takes precedence over global identity):
258
282
 
259
- // Delete a post (only the author can delete)
260
- await kookee.feedback.deletePost('post-id', { externalId: 'user-123' });
283
+ ```typescript
284
+ const newPost = await kookee.feedback.createPost({
285
+ title: 'Add dark mode',
286
+ externalUser: { externalId: 'other-user', name: 'John' },
287
+ });
261
288
 
262
- // Delete a comment (only the author can delete)
263
- await kookee.feedback.deleteComment('comment-id', { externalId: 'user-123' });
289
+ const myPosts = await kookee.feedback.listMyPosts({ externalId: 'other-user' });
290
+ await kookee.feedback.deletePost('post-id', { externalId: 'other-user' });
291
+ await kookee.feedback.deleteComment('comment-id', { externalId: 'other-user' });
264
292
  ```
265
293
 
266
294
  ## Config
@@ -387,6 +415,7 @@ import type {
387
415
  FeedbackPostListItem,
388
416
  FeedbackComment,
389
417
  FeedbackTopContributor,
418
+ KookeeUser,
390
419
  ExternalUser,
391
420
  PublicConfig,
392
421
  PaginatedResponse,
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var k="https://api.kookee.dev",B="Kookee-API-Key",H="Kookee-Project-Id",i=class n extends Error{constructor(t,s,r){super(s);this.code=t;this.status=r;this.name="KookeeApiError",Object.setPrototypeOf(this,n.prototype);}},u=class{baseUrl;apiKey;projectId;constructor(e){this.apiKey=e.apiKey,this.projectId=e.projectId,this.baseUrl=e.baseUrl??k;}getHeaders(){let e={"Content-Type":"application/json"};return this.apiKey&&(e[B]=this.apiKey),this.projectId&&(e[H]=this.projectId),e}async get(e,t){let s=new URL(`${this.baseUrl}${e}`);if(t){for(let[P,o]of Object.entries(t))if(o!=null)if(Array.isArray(o))for(let a of o)s.searchParams.append(P,String(a));else s.searchParams.set(P,String(o));}let r=await fetch(s.toString(),{method:"GET",headers:this.getHeaders()});return this.handleResponse(r)}async post(e,t){let s=await fetch(`${this.baseUrl}${e}`,{method:"POST",headers:this.getHeaders(),body:t?JSON.stringify(t):void 0});return this.handleResponse(s)}async delete(e,t){let s=await fetch(`${this.baseUrl}${e}`,{method:"DELETE",headers:this.getHeaders(),body:t?JSON.stringify(t):void 0});return this.handleResponse(s)}async*streamPost(e,t){let s=await fetch(`${this.baseUrl}${e}`,{method:"POST",headers:{...this.getHeaders(),Accept:"text/event-stream"},body:t?JSON.stringify(t):void 0});if(!s.ok){let a=null;try{a=await s.json();}catch{}throw new i(a?.code??"UNKNOWN_ERROR",a?.message??`Request failed with status ${s.status}`,s.status)}if(!s.body)return;let r=s.body.getReader(),P=new TextDecoder,o="";try{for(;;){let{done:a,value:f}=await r.read();if(a)break;o+=P.decode(f,{stream:!0});let b=o.split(`
2
- `);o=b.pop()??"";for(let I of b){let y=I.trim();if(!(!y||y.startsWith(":"))&&y.startsWith("data: ")){let R=y.slice(6);if(R==="[DONE]")return;yield JSON.parse(R);}}}}finally{r.releaseLock();}}async handleResponse(e){if(!e.ok){let t=null;try{t=await e.json();}catch{}throw new i(t?.code??"UNKNOWN_ERROR",t?.message??`Request failed with status ${e.status}`,e.status)}return e.json()}};var p=class{constructor(e){this.http=e;}async list(e){let{excludeIds:t,...s}=e??{},r=t?.length?{...s,excludeIds:t.join(",")}:s;return this.http.get("/v1/announcements",r)}async getById(e,t){return this.http.get(`/v1/announcements/${encodeURIComponent(e)}`,t)}async getTranslationsById(e){return this.http.get(`/v1/announcements/${encodeURIComponent(e)}/translations`)}};var c=class{constructor(e){this.http=e;}async list(e){return this.http.get("/v1/blog/posts",e)}async getBySlug(e,t){return this.http.get(`/v1/blog/posts/${encodeURIComponent(e)}`,t)}async getById(e,t){return this.http.get(`/v1/blog/posts/by-id/${encodeURIComponent(e)}`,t)}async getTags(){return this.http.get("/v1/blog/tags")}async getTranslationsById(e){return this.http.get(`/v1/blog/posts/by-id/${encodeURIComponent(e)}/translations`)}async getTranslationsBySlug(e){return this.http.get(`/v1/blog/posts/${encodeURIComponent(e)}/translations`)}async react(e,t){return this.http.post(`/v1/blog/posts/${encodeURIComponent(e)}/reactions`,t)}};var l=class{constructor(e){this.http=e;}async list(e){return this.http.get("/v1/changelog",e)}async getBySlug(e,t){return this.http.get(`/v1/changelog/${encodeURIComponent(e)}`,t)}async getById(e,t){return this.http.get(`/v1/changelog/by-id/${encodeURIComponent(e)}`,t)}async getTranslationsById(e){return this.http.get(`/v1/changelog/by-id/${encodeURIComponent(e)}/translations`)}async getTranslationsBySlug(e){return this.http.get(`/v1/changelog/${encodeURIComponent(e)}/translations`)}async react(e,t){return this.http.post(`/v1/changelog/${encodeURIComponent(e)}/reactions`,t)}};var g=class{constructor(e){this.http=e;}async getByKey(e){return this.http.get(`/v1/config/${encodeURIComponent(e)}`)}async list(e){return this.http.get("/v1/config",e)}};var m=class{constructor(e){this.http=e;}async list(e){return this.http.get("/v1/feedback",e)}async getById(e){return this.http.get(`/v1/feedback/by-id/${encodeURIComponent(e)}`)}async vote(e,t){return this.http.post(`/v1/feedback/${encodeURIComponent(e)}/vote`,t)}async getTopContributors(e){return this.http.get("/v1/feedback/top-contributors",e)}async createPost(e){return this.http.post("/v1/feedback",e)}async createComment(e,t){return this.http.post(`/v1/feedback/${encodeURIComponent(e)}/comments`,t)}async listMyPosts(e){return this.http.get("/v1/feedback/mine",e)}async deletePost(e,t){return this.http.delete(`/v1/feedback/${encodeURIComponent(e)}`,t)}async deleteComment(e,t){return this.http.delete(`/v1/feedback/comments/${encodeURIComponent(e)}`,t)}};var d=class{constructor(e){this.http=e;}async categories(e){return this.http.get("/v1/help/categories",e)}async list(e){return this.http.get("/v1/help/articles",e)}async getBySlug(e,t){return this.http.get(`/v1/help/articles/${encodeURIComponent(e)}`,t)}async getById(e,t){return this.http.get(`/v1/help/articles/by-id/${encodeURIComponent(e)}`,t)}async search(e){return this.http.get("/v1/help/search",e)}async getTranslationsById(e){return this.http.get(`/v1/help/articles/by-id/${encodeURIComponent(e)}/translations`)}async getTranslationsBySlug(e){return this.http.get(`/v1/help/articles/${encodeURIComponent(e)}/translations`)}async chat(e){return this.http.post("/v1/help/chat",e)}chatStream(e){return this.http.streamPost("/v1/help/chat/stream",e)}async voteUsefulness(e,t,s){return this.http.post(`/v1/help/articles/by-id/${encodeURIComponent(e)}/vote-usefulness`,{vote:t,previousVote:s})}};var h=class{constructor(e){this.http=e;}async list(e){return this.http.get("/v1/pages",e)}async getBySlug(e,t){return this.http.get(`/v1/pages/${encodeURIComponent(e)}`,t)}async getById(e,t){return this.http.get(`/v1/pages/by-id/${encodeURIComponent(e)}`,t)}async getTranslationsById(e){return this.http.get(`/v1/pages/by-id/${encodeURIComponent(e)}/translations`)}async getTranslationsBySlug(e){return this.http.get(`/v1/pages/${encodeURIComponent(e)}/translations`)}};var C=class{http;announcements;blog;changelog;config;feedback;help;pages;constructor(e){if(!e.apiKey&&!e.projectId)throw new Error("Either apiKey or projectId is required");this.http=new u({apiKey:e.apiKey,projectId:e.projectId,baseUrl:e.baseUrl}),this.announcements=new p(this.http),this.blog=new c(this.http),this.changelog=new l(this.http),this.config=new g(this.http),this.feedback=new m(this.http),this.help=new d(this.http),this.pages=new h(this.http);}async health(){return this.http.get("/v1/health")}};exports.AnnouncementModule=p;exports.BlogModule=c;exports.ChangelogModule=l;exports.ConfigModule=g;exports.FeedbackModule=m;exports.HelpModule=d;exports.Kookee=C;exports.KookeeApiError=i;exports.PagesModule=h;
1
+ 'use strict';var k="https://api.kookee.dev",B="Kookee-API-Key",I="Kookee-Project-Id",i=class s extends Error{constructor(t,r,o){super(r);this.code=t;this.status=o;this.name="KookeeApiError",Object.setPrototypeOf(this,s.prototype);}},C=class{baseUrl;apiKey;projectId;constructor(e){this.apiKey=e.apiKey,this.projectId=e.projectId,this.baseUrl=e.baseUrl??k;}getHeaders(){let e={"Content-Type":"application/json"};return this.apiKey&&(e[B]=this.apiKey),this.projectId&&(e[I]=this.projectId),e}async get(e,t){let r=new URL(`${this.baseUrl}${e}`);if(t){for(let[h,n]of Object.entries(t))if(n!=null)if(Array.isArray(n))for(let a of n)r.searchParams.append(h,String(a));else r.searchParams.set(h,String(n));}let o=await fetch(r.toString(),{method:"GET",headers:this.getHeaders()});return this.handleResponse(o)}async post(e,t){let r=await fetch(`${this.baseUrl}${e}`,{method:"POST",headers:this.getHeaders(),body:t?JSON.stringify(t):void 0});return this.handleResponse(r)}async delete(e,t){let r=await fetch(`${this.baseUrl}${e}`,{method:"DELETE",headers:this.getHeaders(),body:t?JSON.stringify(t):void 0});return this.handleResponse(r)}async*streamPost(e,t){let r=await fetch(`${this.baseUrl}${e}`,{method:"POST",headers:{...this.getHeaders(),Accept:"text/event-stream"},body:t?JSON.stringify(t):void 0});if(!r.ok){let a=null;try{a=await r.json();}catch{}throw new i(a?.code??"UNKNOWN_ERROR",a?.message??`Request failed with status ${r.status}`,r.status)}if(!r.body)return;let o=r.body.getReader(),h=new TextDecoder,n="";try{for(;;){let{done:a,value:b}=await o.read();if(a)break;n+=h.decode(b,{stream:!0});let E=n.split(`
2
+ `);n=E.pop()??"";for(let x of E){let u=x.trim();if(!(!u||u.startsWith(":"))&&u.startsWith("data: ")){let R=u.slice(6);if(R==="[DONE]")return;yield JSON.parse(R);}}}}finally{o.releaseLock();}}async handleResponse(e){if(!e.ok){let t=null;try{t=await e.json();}catch{}throw new i(t?.code??"UNKNOWN_ERROR",t?.message??`Request failed with status ${e.status}`,e.status)}return e.json()}};var m=class{constructor(e){this.entries=e;}async list(e){return this.entries.list({type:"announcement",...e})}async getById(e,t){return this.entries.getById(e,t)}async getTranslationsById(e){return this.entries.getTranslationsById(e)}async getComments(e,t){return this.entries.getComments(e,t)}};var p=class{constructor(e){this.entries=e;}async list(e){return this.entries.list({type:"blog",...e})}async getBySlug(e,t){return this.entries.getBySlug(e,{type:"blog",...t})}async getById(e,t){return this.entries.getById(e,t)}async getTags(){return this.entries.getTags("blog")}async getTranslationsById(e){return this.entries.getTranslationsById(e)}async getTranslationsBySlug(e){return this.entries.getTranslationsBySlug(e)}async getComments(e,t){return this.entries.getComments(e,t)}async react(e,t){return this.entries.react(e,t)}};var c=class{constructor(e){this.entries=e;}async list(e){return this.entries.list({type:"changelog",...e})}async getBySlug(e,t){return this.entries.getBySlug(e,{type:"changelog",...t})}async getById(e,t){return this.entries.getById(e,t)}async getTranslationsById(e){return this.entries.getTranslationsById(e)}async getTranslationsBySlug(e){return this.entries.getTranslationsBySlug(e)}async getComments(e,t){return this.entries.getComments(e,t)}async react(e,t){return this.entries.react(e,t)}};var g=class{constructor(e){this.http=e;}async getByKey(e){return this.http.get(`/v1/config/${encodeURIComponent(e)}`)}async list(e){return this.http.get("/v1/config",e)}};var l=class{constructor(e){this.http=e;}async list(e){return this.http.get("/v1/entries",e)}async getById(e,t){return this.http.get(`/v1/entries/by-id/${encodeURIComponent(e)}`,t)}async getBySlug(e,t){return this.http.get(`/v1/entries/${encodeURIComponent(e)}`,t)}async getTranslationsById(e){return this.http.get(`/v1/entries/by-id/${encodeURIComponent(e)}/translations`)}async getTranslationsBySlug(e){return this.http.get(`/v1/entries/${encodeURIComponent(e)}/translations`)}async getComments(e,t){return this.http.get(`/v1/entries/${encodeURIComponent(e)}/comments`,t)}async react(e,t){return this.http.post(`/v1/entries/${encodeURIComponent(e)}/reactions`,t)}async getTags(e){return this.http.get("/v1/tags",{type:e})}async getCategories(e,t){return this.http.get("/v1/categories",{type:e,...t})}};var y=class{constructor(e,t){this.http=e;this.getUserContext=t;}async list(e){return this.http.get("/v1/feedback",e)}async getById(e){return this.http.get(`/v1/feedback/by-id/${encodeURIComponent(e)}`)}async vote(e,t){return this.http.post(`/v1/feedback/${encodeURIComponent(e)}/vote`,t)}async getTopContributors(e){return this.http.get("/v1/feedback/top-contributors",e)}async createPost(e){let t=e.externalUser??this.getUserContext();if(!t)throw new Error("No user identified. Call kookee.identify() first or pass externalUser in params.");return this.http.post("/v1/feedback",{...e,externalUser:t})}async createComment(e,t){let r=t.externalUser??this.getUserContext();if(!r)throw new Error("No user identified. Call kookee.identify() first or pass externalUser in params.");return this.http.post(`/v1/feedback/${encodeURIComponent(e)}/comments`,{...t,externalUser:r})}async listMyPosts(e){let t=e?.externalId??this.getUserContext()?.externalId;if(!t)throw new Error("No user identified. Call kookee.identify() first or pass externalId in params.");return this.http.get("/v1/feedback/mine",{...e,externalId:t})}async deletePost(e,t){let r=t?.externalId??this.getUserContext()?.externalId;if(!r)throw new Error("No user identified. Call kookee.identify() first or pass externalId in params.");return this.http.delete(`/v1/feedback/${encodeURIComponent(e)}`,{externalId:r})}async deleteComment(e,t){let r=t?.externalId??this.getUserContext()?.externalId;if(!r)throw new Error("No user identified. Call kookee.identify() first or pass externalId in params.");return this.http.delete(`/v1/feedback/comments/${encodeURIComponent(e)}`,{externalId:r})}};var d=class{http;entries;constructor(e,t){this.http=e,this.entries=t;}async categories(e){return this.entries.getCategories("help_article",e)}async list(e){return this.entries.list({type:"help_article",...e})}async getBySlug(e,t){return this.entries.getBySlug(e,{type:"help_article",...t})}async getById(e,t){return this.entries.getById(e,t)}async search(e){return this.http.get("/v1/help/search",e)}async getTranslationsById(e){return this.entries.getTranslationsById(e)}async getTranslationsBySlug(e){return this.entries.getTranslationsBySlug(e)}async getComments(e,t){return this.entries.getComments(e,t)}async react(e,t){return this.entries.react(e,t)}async chat(e){return this.http.post("/v1/help/chat",e)}chatStream(e){return this.http.streamPost("/v1/help/chat/stream",e)}};var P=class{constructor(e){this.entries=e;}async list(e){return this.entries.list({type:"page",...e})}async getBySlug(e,t){return this.entries.getBySlug(e,{type:"page",...t})}async getById(e,t){return this.entries.getById(e,t)}async getTranslationsById(e){return this.entries.getTranslationsById(e)}async getTranslationsBySlug(e){return this.entries.getTranslationsBySlug(e)}async getComments(e,t){return this.entries.getComments(e,t)}};var f=class{http;user=null;entries;announcements;blog;changelog;config;feedback;help;pages;constructor(e){if(!e.apiKey&&!e.projectId)throw new Error("Either apiKey or projectId is required");this.http=new C({apiKey:e.apiKey,projectId:e.projectId,baseUrl:e.baseUrl}),this.entries=new l(this.http),this.announcements=new m(this.entries),this.blog=new p(this.entries),this.changelog=new c(this.entries),this.config=new g(this.http),this.feedback=new y(this.http,()=>this.user),this.help=new d(this.http,this.entries),this.pages=new P(this.entries);}identify(e){this.user=e;}reset(){this.user=null;}getUser(){return this.user}async health(){return this.http.get("/v1/health")}};exports.AnnouncementModule=m;exports.BlogModule=p;exports.ChangelogModule=c;exports.ConfigModule=g;exports.EntriesModule=l;exports.FeedbackModule=y;exports.HelpModule=d;exports.Kookee=f;exports.KookeeApiError=i;exports.PagesModule=P;