@khoralabs/chat 0.1.0 → 0.1.1

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/dist/errors.d.ts CHANGED
@@ -17,4 +17,6 @@ export declare class ChatValidationError extends ChatError {
17
17
  export declare class ChatPermissionError extends ChatError {
18
18
  constructor(message: string);
19
19
  }
20
+ /** Duck-typed check — safe across multi-entrypoint bundles where `instanceof` can fail. */
21
+ export declare function isChatNotFoundError(error: unknown): error is ChatNotFoundError;
20
22
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAMjD;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;gBAGrF,MAAM,EAAE,eAAe,GAAG,sBAAsB,GAAG,0BAA0B,EAC7E,OAAO,EAAE,MAAM;CAMlB;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM;CAI5B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAMjD;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;gBAGrF,MAAM,EAAE,eAAe,GAAG,sBAAsB,GAAG,0BAA0B,EAC7E,OAAO,EAAE,MAAM;CAMlB;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAS9E"}
@@ -44,6 +44,9 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
49
+ }
47
50
  // src/hash.ts
48
51
  function sha256Hex(input) {
49
52
  const hasher = new Bun.CryptoHasher("sha256");
@@ -44,6 +44,9 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
49
+ }
47
50
  // src/hash.ts
48
51
  function sha256Hex(input) {
49
52
  const hasher = new Bun.CryptoHasher("sha256");
@@ -398,7 +401,7 @@ function stringField(body, key) {
398
401
  return typeof value === "string" && value.length > 0 ? value : null;
399
402
  }
400
403
  function errorResponse(error) {
401
- if (error instanceof ChatNotFoundError) {
404
+ if (isChatNotFoundError(error)) {
402
405
  return json({ error: error.message }, { status: 404 });
403
406
  }
404
407
  const message = error instanceof Error ? error.message : String(error);
@@ -959,7 +962,7 @@ async function createChatStorage(config) {
959
962
  };
960
963
  }
961
964
  function isChatNotFound(error) {
962
- return error instanceof ChatNotFoundError;
965
+ return isChatNotFoundError(error);
963
966
  }
964
967
 
965
968
  // src/http/server.ts
@@ -44,228 +44,10 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
- // src/hash.ts
48
- function sha256Hex(input) {
49
- const hasher = new Bun.CryptoHasher("sha256");
50
- hasher.update(input);
51
- return hasher.digest("hex");
52
- }
53
- function stableStringify(value) {
54
- if (value === undefined) {
55
- return "null";
56
- }
57
- if (value === null || typeof value !== "object") {
58
- return JSON.stringify(value);
59
- }
60
- if (Array.isArray(value)) {
61
- return `[${value.map((item) => stableStringify(item)).join(",")}]`;
62
- }
63
- const record = value;
64
- const keys = Object.keys(record).sort();
65
- return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
66
- }
67
- function canonicalPostVersionPayload(input) {
68
- return stableStringify({
69
- postId: input.postId,
70
- versionId: input.versionId,
71
- threadId: input.threadId,
72
- author: input.author,
73
- role: input.role,
74
- parts: input.parts,
75
- metadata: input.metadata ?? null,
76
- mentions: input.mentions ?? null,
77
- model: input.model ?? null,
78
- usage: input.usage ?? null,
79
- parentVersionId: input.parentVersionId ?? null,
80
- previousPostVersionId: input.previousPostVersionId ?? null
81
- });
82
- }
83
- function computeContentHash(payload) {
84
- return sha256Hex(canonicalPostVersionPayload(payload));
85
- }
86
- function computeLineageHash(input) {
87
- return sha256Hex(stableStringify({
88
- previousLineageHash: input.previousLineageHash,
89
- contentHash: input.contentHash,
90
- postId: input.postId,
91
- versionId: input.versionId
92
- }));
93
- }
94
- function canonicalAclEventPayload(input) {
95
- return stableStringify({
96
- type: input.type,
97
- targetType: input.targetType,
98
- targetId: input.targetId,
99
- scope: input.scope ?? null,
100
- role: input.role ?? null,
101
- actor: input.actor,
102
- previousAclEventId: input.previousAclEventId ?? null
103
- });
104
- }
105
- function computeAclEventContentHash(payload) {
106
- return sha256Hex(canonicalAclEventPayload(payload));
107
- }
108
- function signedPayloadBytes(payload) {
109
- return new TextEncoder().encode(payload);
110
- }
111
- function canonicalSignedPostVersionPayload(input) {
112
- return stableStringify({
113
- contentHash: input.contentHash,
114
- lineageHash: input.lineageHash,
115
- payload: JSON.parse(canonicalPostVersionPayload(input))
116
- });
117
- }
118
- function canonicalSignedAclEventPayload(input) {
119
- return stableStringify({
120
- contentHash: input.contentHash,
121
- payload: JSON.parse(canonicalAclEventPayload(input.payload))
122
- });
123
- }
124
- async function maybeSignPayload(payload, signer, sign) {
125
- return sign(signedPayloadBytes(payload), signer);
126
- }
127
- // src/ids.ts
128
- function createId() {
129
- return crypto.randomUUID();
130
- }
131
- function scopeKey(scope) {
132
- return `${scope.type}:${scope.id}`;
133
- }
134
- function scopeRefFromKey(key) {
135
- const separatorIndex = key.indexOf(":");
136
- if (separatorIndex === -1) {
137
- throw new Error(`Invalid scope key: ${key}`);
138
- }
139
- return {
140
- type: key.slice(0, separatorIndex),
141
- id: key.slice(separatorIndex + 1)
142
- };
143
- }
144
- // src/lineage.ts
145
- function walkLineageFromHead(headVersionId, versionsById) {
146
- const lineage = [];
147
- let currentId = headVersionId;
148
- const seenPostIds = new Set;
149
- while (currentId) {
150
- const version = versionsById.get(currentId);
151
- if (!version)
152
- break;
153
- if (!seenPostIds.has(version.postId)) {
154
- lineage.push(version);
155
- seenPostIds.add(version.postId);
156
- }
157
- currentId = version.previousPostVersionId;
158
- }
159
- return lineage.reverse();
160
- }
161
- function lineageBetween(headVersionId, ancestorVersionId, versionsById) {
162
- const lineage = walkLineageFromHead(headVersionId, versionsById);
163
- const startIndex = lineage.findIndex((version) => version.id === ancestorVersionId);
164
- if (startIndex === -1)
165
- return null;
166
- return lineage.slice(startIndex);
167
- }
168
- function postFromVersion(version, index, deletedAtMs) {
169
- return {
170
- ...version,
171
- id: version.postId,
172
- status: "complete",
173
- versionId: version.id,
174
- previousVersionId: version.parentVersionId ?? null,
175
- index,
176
- updatedAtMs: version.parentVersionId ? version.createdAtMs : null,
177
- deletedAtMs: deletedAtMs ?? null
178
- };
179
- }
180
- // src/stream.ts
181
- function rebuildStreamCacheFromEvents(events) {
182
- const sorted = [...events].sort((a, b) => a.revision - b.revision);
183
- let message = null;
184
- let mentions;
185
- let model;
186
- let usage;
187
- let revision = 0;
188
- for (const event of sorted) {
189
- if (event.eventType === "stream.started") {
190
- if (!event.message) {
191
- throw new Error(`stream.started event ${event.id} missing message`);
192
- }
193
- message = event.message;
194
- mentions = event.mentions;
195
- model = event.model;
196
- usage = event.usage;
197
- revision = event.revision;
198
- continue;
199
- }
200
- if (event.eventType === "stream.delta") {
201
- if (!event.message) {
202
- throw new Error(`stream.delta event ${event.id} missing message`);
203
- }
204
- message = event.message;
205
- if (event.mentions !== undefined)
206
- mentions = event.mentions;
207
- if (event.model !== undefined)
208
- model = event.model;
209
- if (event.usage !== undefined)
210
- usage = event.usage;
211
- revision = event.revision;
212
- continue;
213
- }
214
- if (event.eventType === "stream.completed" || event.eventType === "stream.aborted") {
215
- revision = event.revision;
216
- }
217
- }
218
- if (!message) {
219
- throw new Error("stream events did not produce a message");
220
- }
221
- return { message, mentions, model, usage, revision };
222
- }
223
- function streamingPostFromCache(input) {
224
- return {
225
- ...input.message,
226
- id: input.postId,
227
- status: "streaming",
228
- threadId: input.threadId,
229
- author: input.author,
230
- mentions: input.mentions,
231
- model: input.model,
232
- usage: input.usage,
233
- index: input.index,
234
- streamRevision: input.streamRevision,
235
- createdAtMs: input.createdAtMs,
236
- updatedAtMs: input.updatedAtMs ?? null,
237
- deletedAtMs: input.deletedAtMs ?? null
238
- };
239
- }
240
- function mergeThreadPostsForList(lineagePosts, activePosts) {
241
- const byIndex = new Map;
242
- for (const item of lineagePosts) {
243
- byIndex.set(item.index, item.post);
244
- }
245
- for (const post of activePosts) {
246
- if (post.status === "streaming" || post.status === "aborted") {
247
- byIndex.set(post.index, post);
248
- }
249
- }
250
- return [...byIndex.entries()].sort(([a], [b]) => a - b).map(([, post]) => post);
251
- }
252
- // src/validation.ts
253
- function assertThreadRoot(root) {
254
- if (root.type === "channel") {
255
- if (!root.channelId.trim()) {
256
- throw new ChatValidationError("channel root requires channelId");
257
- }
258
- return;
259
- }
260
- if (!root.postId.trim()) {
261
- throw new ChatValidationError("post root requires postId");
262
- }
263
- }
264
- function assertNonEmptyString(value, label) {
265
- if (!value.trim()) {
266
- throw new ChatValidationError(`${label} is required`);
267
- }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
268
49
  }
50
+
269
51
  // src/http/routes.ts
270
52
  function json(value, init) {
271
53
  return Response.json(value, init);
@@ -301,7 +83,7 @@ function stringField(body, key) {
301
83
  return typeof value === "string" && value.length > 0 ? value : null;
302
84
  }
303
85
  function errorResponse(error) {
304
- if (error instanceof ChatNotFoundError) {
86
+ if (isChatNotFoundError(error)) {
305
87
  return json({ error: error.message }, { status: 404 });
306
88
  }
307
89
  const message = error instanceof Error ? error.message : String(error);
@@ -44,228 +44,10 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
- // src/hash.ts
48
- function sha256Hex(input) {
49
- const hasher = new Bun.CryptoHasher("sha256");
50
- hasher.update(input);
51
- return hasher.digest("hex");
52
- }
53
- function stableStringify(value) {
54
- if (value === undefined) {
55
- return "null";
56
- }
57
- if (value === null || typeof value !== "object") {
58
- return JSON.stringify(value);
59
- }
60
- if (Array.isArray(value)) {
61
- return `[${value.map((item) => stableStringify(item)).join(",")}]`;
62
- }
63
- const record = value;
64
- const keys = Object.keys(record).sort();
65
- return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
66
- }
67
- function canonicalPostVersionPayload(input) {
68
- return stableStringify({
69
- postId: input.postId,
70
- versionId: input.versionId,
71
- threadId: input.threadId,
72
- author: input.author,
73
- role: input.role,
74
- parts: input.parts,
75
- metadata: input.metadata ?? null,
76
- mentions: input.mentions ?? null,
77
- model: input.model ?? null,
78
- usage: input.usage ?? null,
79
- parentVersionId: input.parentVersionId ?? null,
80
- previousPostVersionId: input.previousPostVersionId ?? null
81
- });
82
- }
83
- function computeContentHash(payload) {
84
- return sha256Hex(canonicalPostVersionPayload(payload));
85
- }
86
- function computeLineageHash(input) {
87
- return sha256Hex(stableStringify({
88
- previousLineageHash: input.previousLineageHash,
89
- contentHash: input.contentHash,
90
- postId: input.postId,
91
- versionId: input.versionId
92
- }));
93
- }
94
- function canonicalAclEventPayload(input) {
95
- return stableStringify({
96
- type: input.type,
97
- targetType: input.targetType,
98
- targetId: input.targetId,
99
- scope: input.scope ?? null,
100
- role: input.role ?? null,
101
- actor: input.actor,
102
- previousAclEventId: input.previousAclEventId ?? null
103
- });
104
- }
105
- function computeAclEventContentHash(payload) {
106
- return sha256Hex(canonicalAclEventPayload(payload));
107
- }
108
- function signedPayloadBytes(payload) {
109
- return new TextEncoder().encode(payload);
110
- }
111
- function canonicalSignedPostVersionPayload(input) {
112
- return stableStringify({
113
- contentHash: input.contentHash,
114
- lineageHash: input.lineageHash,
115
- payload: JSON.parse(canonicalPostVersionPayload(input))
116
- });
117
- }
118
- function canonicalSignedAclEventPayload(input) {
119
- return stableStringify({
120
- contentHash: input.contentHash,
121
- payload: JSON.parse(canonicalAclEventPayload(input.payload))
122
- });
123
- }
124
- async function maybeSignPayload(payload, signer, sign) {
125
- return sign(signedPayloadBytes(payload), signer);
126
- }
127
- // src/ids.ts
128
- function createId() {
129
- return crypto.randomUUID();
130
- }
131
- function scopeKey(scope) {
132
- return `${scope.type}:${scope.id}`;
133
- }
134
- function scopeRefFromKey(key) {
135
- const separatorIndex = key.indexOf(":");
136
- if (separatorIndex === -1) {
137
- throw new Error(`Invalid scope key: ${key}`);
138
- }
139
- return {
140
- type: key.slice(0, separatorIndex),
141
- id: key.slice(separatorIndex + 1)
142
- };
143
- }
144
- // src/lineage.ts
145
- function walkLineageFromHead(headVersionId, versionsById) {
146
- const lineage = [];
147
- let currentId = headVersionId;
148
- const seenPostIds = new Set;
149
- while (currentId) {
150
- const version = versionsById.get(currentId);
151
- if (!version)
152
- break;
153
- if (!seenPostIds.has(version.postId)) {
154
- lineage.push(version);
155
- seenPostIds.add(version.postId);
156
- }
157
- currentId = version.previousPostVersionId;
158
- }
159
- return lineage.reverse();
160
- }
161
- function lineageBetween(headVersionId, ancestorVersionId, versionsById) {
162
- const lineage = walkLineageFromHead(headVersionId, versionsById);
163
- const startIndex = lineage.findIndex((version) => version.id === ancestorVersionId);
164
- if (startIndex === -1)
165
- return null;
166
- return lineage.slice(startIndex);
167
- }
168
- function postFromVersion(version, index, deletedAtMs) {
169
- return {
170
- ...version,
171
- id: version.postId,
172
- status: "complete",
173
- versionId: version.id,
174
- previousVersionId: version.parentVersionId ?? null,
175
- index,
176
- updatedAtMs: version.parentVersionId ? version.createdAtMs : null,
177
- deletedAtMs: deletedAtMs ?? null
178
- };
179
- }
180
- // src/stream.ts
181
- function rebuildStreamCacheFromEvents(events) {
182
- const sorted = [...events].sort((a, b) => a.revision - b.revision);
183
- let message = null;
184
- let mentions;
185
- let model;
186
- let usage;
187
- let revision = 0;
188
- for (const event of sorted) {
189
- if (event.eventType === "stream.started") {
190
- if (!event.message) {
191
- throw new Error(`stream.started event ${event.id} missing message`);
192
- }
193
- message = event.message;
194
- mentions = event.mentions;
195
- model = event.model;
196
- usage = event.usage;
197
- revision = event.revision;
198
- continue;
199
- }
200
- if (event.eventType === "stream.delta") {
201
- if (!event.message) {
202
- throw new Error(`stream.delta event ${event.id} missing message`);
203
- }
204
- message = event.message;
205
- if (event.mentions !== undefined)
206
- mentions = event.mentions;
207
- if (event.model !== undefined)
208
- model = event.model;
209
- if (event.usage !== undefined)
210
- usage = event.usage;
211
- revision = event.revision;
212
- continue;
213
- }
214
- if (event.eventType === "stream.completed" || event.eventType === "stream.aborted") {
215
- revision = event.revision;
216
- }
217
- }
218
- if (!message) {
219
- throw new Error("stream events did not produce a message");
220
- }
221
- return { message, mentions, model, usage, revision };
222
- }
223
- function streamingPostFromCache(input) {
224
- return {
225
- ...input.message,
226
- id: input.postId,
227
- status: "streaming",
228
- threadId: input.threadId,
229
- author: input.author,
230
- mentions: input.mentions,
231
- model: input.model,
232
- usage: input.usage,
233
- index: input.index,
234
- streamRevision: input.streamRevision,
235
- createdAtMs: input.createdAtMs,
236
- updatedAtMs: input.updatedAtMs ?? null,
237
- deletedAtMs: input.deletedAtMs ?? null
238
- };
239
- }
240
- function mergeThreadPostsForList(lineagePosts, activePosts) {
241
- const byIndex = new Map;
242
- for (const item of lineagePosts) {
243
- byIndex.set(item.index, item.post);
244
- }
245
- for (const post of activePosts) {
246
- if (post.status === "streaming" || post.status === "aborted") {
247
- byIndex.set(post.index, post);
248
- }
249
- }
250
- return [...byIndex.entries()].sort(([a], [b]) => a - b).map(([, post]) => post);
251
- }
252
- // src/validation.ts
253
- function assertThreadRoot(root) {
254
- if (root.type === "channel") {
255
- if (!root.channelId.trim()) {
256
- throw new ChatValidationError("channel root requires channelId");
257
- }
258
- return;
259
- }
260
- if (!root.postId.trim()) {
261
- throw new ChatValidationError("post root requires postId");
262
- }
263
- }
264
- function assertNonEmptyString(value, label) {
265
- if (!value.trim()) {
266
- throw new ChatValidationError(`${label} is required`);
267
- }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
268
49
  }
50
+
269
51
  // src/http/routes.ts
270
52
  function json(value, init) {
271
53
  return Response.json(value, init);
@@ -301,7 +83,7 @@ function stringField(body, key) {
301
83
  return typeof value === "string" && value.length > 0 ? value : null;
302
84
  }
303
85
  function errorResponse(error) {
304
- if (error instanceof ChatNotFoundError) {
86
+ if (isChatNotFoundError(error)) {
305
87
  return json({ error: error.message }, { status: 404 });
306
88
  }
307
89
  const message = error instanceof Error ? error.message : String(error);
@@ -645,6 +427,24 @@ import {
645
427
  ensureChatSchema
646
428
  } from "@khoralabs/chat/turso-serverless";
647
429
 
430
+ // src/ids.ts
431
+ function createId() {
432
+ return crypto.randomUUID();
433
+ }
434
+ function scopeKey(scope) {
435
+ return `${scope.type}:${scope.id}`;
436
+ }
437
+ function scopeRefFromKey(key) {
438
+ const separatorIndex = key.indexOf(":");
439
+ if (separatorIndex === -1) {
440
+ throw new Error(`Invalid scope key: ${key}`);
441
+ }
442
+ return {
443
+ type: key.slice(0, separatorIndex),
444
+ id: key.slice(separatorIndex + 1)
445
+ };
446
+ }
447
+
648
448
  // src/service.ts
649
449
  function createChatService(persistence, options = {}) {
650
450
  const emit = (event) => {
@@ -862,7 +662,7 @@ async function createChatStorage(config) {
862
662
  };
863
663
  }
864
664
  function isChatNotFound(error) {
865
- return error instanceof ChatNotFoundError;
665
+ return isChatNotFoundError(error);
866
666
  }
867
667
 
868
668
  // src/http/server.ts
@@ -1,5 +1,5 @@
1
1
  import type { ChatEvent, ChatPersistence } from "../domain.js";
2
- import { ChatNotFoundError } from "../domain.js";
2
+ import { type ChatNotFoundError } from "../errors.js";
3
3
  import { type ChatService } from "../service.js";
4
4
  export type ChatHttpRuntime = {
5
5
  service: ChatService;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/http/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,eAAe,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClF,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,WAAW,EAAE,eAAe,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,eAAe,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,eAAe,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,2FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,eAAe,CA0B5F;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CA+BvF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAEzE"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/http/service.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,eAAe,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClF,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,WAAW,EAAE,eAAe,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,eAAe,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,eAAe,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEF,2FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,eAAe,CA0B5F;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CA+BvF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAEzE"}
@@ -53,86 +53,10 @@ class ChatPermissionError extends ChatError {
53
53
  this.name = "ChatPermissionError";
54
54
  }
55
55
  }
56
- // src/hash.ts
57
- function sha256Hex(input) {
58
- const hasher = new Bun.CryptoHasher("sha256");
59
- hasher.update(input);
60
- return hasher.digest("hex");
61
- }
62
- function stableStringify(value) {
63
- if (value === undefined) {
64
- return "null";
65
- }
66
- if (value === null || typeof value !== "object") {
67
- return JSON.stringify(value);
68
- }
69
- if (Array.isArray(value)) {
70
- return `[${value.map((item) => stableStringify(item)).join(",")}]`;
71
- }
72
- const record = value;
73
- const keys = Object.keys(record).sort();
74
- return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
75
- }
76
- function canonicalPostVersionPayload(input) {
77
- return stableStringify({
78
- postId: input.postId,
79
- versionId: input.versionId,
80
- threadId: input.threadId,
81
- author: input.author,
82
- role: input.role,
83
- parts: input.parts,
84
- metadata: input.metadata ?? null,
85
- mentions: input.mentions ?? null,
86
- model: input.model ?? null,
87
- usage: input.usage ?? null,
88
- parentVersionId: input.parentVersionId ?? null,
89
- previousPostVersionId: input.previousPostVersionId ?? null
90
- });
91
- }
92
- function computeContentHash(payload) {
93
- return sha256Hex(canonicalPostVersionPayload(payload));
94
- }
95
- function computeLineageHash(input) {
96
- return sha256Hex(stableStringify({
97
- previousLineageHash: input.previousLineageHash,
98
- contentHash: input.contentHash,
99
- postId: input.postId,
100
- versionId: input.versionId
101
- }));
102
- }
103
- function canonicalAclEventPayload(input) {
104
- return stableStringify({
105
- type: input.type,
106
- targetType: input.targetType,
107
- targetId: input.targetId,
108
- scope: input.scope ?? null,
109
- role: input.role ?? null,
110
- actor: input.actor,
111
- previousAclEventId: input.previousAclEventId ?? null
112
- });
113
- }
114
- function computeAclEventContentHash(payload) {
115
- return sha256Hex(canonicalAclEventPayload(payload));
116
- }
117
- function signedPayloadBytes(payload) {
118
- return new TextEncoder().encode(payload);
119
- }
120
- function canonicalSignedPostVersionPayload(input) {
121
- return stableStringify({
122
- contentHash: input.contentHash,
123
- lineageHash: input.lineageHash,
124
- payload: JSON.parse(canonicalPostVersionPayload(input))
125
- });
126
- }
127
- function canonicalSignedAclEventPayload(input) {
128
- return stableStringify({
129
- contentHash: input.contentHash,
130
- payload: JSON.parse(canonicalAclEventPayload(input.payload))
131
- });
132
- }
133
- async function maybeSignPayload(payload, signer, sign) {
134
- return sign(signedPayloadBytes(payload), signer);
56
+ function isChatNotFoundError(error) {
57
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
135
58
  }
59
+
136
60
  // src/ids.ts
137
61
  function createId() {
138
62
  return crypto.randomUUID();
@@ -150,131 +74,7 @@ function scopeRefFromKey(key) {
150
74
  id: key.slice(separatorIndex + 1)
151
75
  };
152
76
  }
153
- // src/lineage.ts
154
- function walkLineageFromHead(headVersionId, versionsById) {
155
- const lineage = [];
156
- let currentId = headVersionId;
157
- const seenPostIds = new Set;
158
- while (currentId) {
159
- const version = versionsById.get(currentId);
160
- if (!version)
161
- break;
162
- if (!seenPostIds.has(version.postId)) {
163
- lineage.push(version);
164
- seenPostIds.add(version.postId);
165
- }
166
- currentId = version.previousPostVersionId;
167
- }
168
- return lineage.reverse();
169
- }
170
- function lineageBetween(headVersionId, ancestorVersionId, versionsById) {
171
- const lineage = walkLineageFromHead(headVersionId, versionsById);
172
- const startIndex = lineage.findIndex((version) => version.id === ancestorVersionId);
173
- if (startIndex === -1)
174
- return null;
175
- return lineage.slice(startIndex);
176
- }
177
- function postFromVersion(version, index, deletedAtMs) {
178
- return {
179
- ...version,
180
- id: version.postId,
181
- status: "complete",
182
- versionId: version.id,
183
- previousVersionId: version.parentVersionId ?? null,
184
- index,
185
- updatedAtMs: version.parentVersionId ? version.createdAtMs : null,
186
- deletedAtMs: deletedAtMs ?? null
187
- };
188
- }
189
- // src/stream.ts
190
- function rebuildStreamCacheFromEvents(events) {
191
- const sorted = [...events].sort((a, b) => a.revision - b.revision);
192
- let message = null;
193
- let mentions;
194
- let model;
195
- let usage;
196
- let revision = 0;
197
- for (const event of sorted) {
198
- if (event.eventType === "stream.started") {
199
- if (!event.message) {
200
- throw new Error(`stream.started event ${event.id} missing message`);
201
- }
202
- message = event.message;
203
- mentions = event.mentions;
204
- model = event.model;
205
- usage = event.usage;
206
- revision = event.revision;
207
- continue;
208
- }
209
- if (event.eventType === "stream.delta") {
210
- if (!event.message) {
211
- throw new Error(`stream.delta event ${event.id} missing message`);
212
- }
213
- message = event.message;
214
- if (event.mentions !== undefined)
215
- mentions = event.mentions;
216
- if (event.model !== undefined)
217
- model = event.model;
218
- if (event.usage !== undefined)
219
- usage = event.usage;
220
- revision = event.revision;
221
- continue;
222
- }
223
- if (event.eventType === "stream.completed" || event.eventType === "stream.aborted") {
224
- revision = event.revision;
225
- }
226
- }
227
- if (!message) {
228
- throw new Error("stream events did not produce a message");
229
- }
230
- return { message, mentions, model, usage, revision };
231
- }
232
- function streamingPostFromCache(input) {
233
- return {
234
- ...input.message,
235
- id: input.postId,
236
- status: "streaming",
237
- threadId: input.threadId,
238
- author: input.author,
239
- mentions: input.mentions,
240
- model: input.model,
241
- usage: input.usage,
242
- index: input.index,
243
- streamRevision: input.streamRevision,
244
- createdAtMs: input.createdAtMs,
245
- updatedAtMs: input.updatedAtMs ?? null,
246
- deletedAtMs: input.deletedAtMs ?? null
247
- };
248
- }
249
- function mergeThreadPostsForList(lineagePosts, activePosts) {
250
- const byIndex = new Map;
251
- for (const item of lineagePosts) {
252
- byIndex.set(item.index, item.post);
253
- }
254
- for (const post of activePosts) {
255
- if (post.status === "streaming" || post.status === "aborted") {
256
- byIndex.set(post.index, post);
257
- }
258
- }
259
- return [...byIndex.entries()].sort(([a], [b]) => a - b).map(([, post]) => post);
260
- }
261
- // src/validation.ts
262
- function assertThreadRoot(root) {
263
- if (root.type === "channel") {
264
- if (!root.channelId.trim()) {
265
- throw new ChatValidationError("channel root requires channelId");
266
- }
267
- return;
268
- }
269
- if (!root.postId.trim()) {
270
- throw new ChatValidationError("post root requires postId");
271
- }
272
- }
273
- function assertNonEmptyString(value, label) {
274
- if (!value.trim()) {
275
- throw new ChatValidationError(`${label} is required`);
276
- }
277
- }
77
+
278
78
  // src/service.ts
279
79
  function createChatService(persistence, options = {}) {
280
80
  const emit = (event) => {
@@ -492,7 +292,7 @@ async function createChatStorage(config) {
492
292
  };
493
293
  }
494
294
  function isChatNotFound(error) {
495
- return error instanceof ChatNotFoundError;
295
+ return isChatNotFoundError(error);
496
296
  }
497
297
  export {
498
298
  isChatNotFound,
package/dist/index.js CHANGED
@@ -44,6 +44,9 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
49
+ }
47
50
  // src/hash.ts
48
51
  function sha256Hex(input) {
49
52
  const hasher = new Bun.CryptoHasher("sha256");
@@ -434,6 +437,7 @@ export {
434
437
  mergeThreadPostsForList,
435
438
  maybeSignPayload,
436
439
  lineageBetween,
440
+ isChatNotFoundError,
437
441
  createId,
438
442
  createChatService,
439
443
  computeLineageHash,
@@ -44,6 +44,9 @@ class ChatPermissionError extends ChatError {
44
44
  this.name = "ChatPermissionError";
45
45
  }
46
46
  }
47
+ function isChatNotFoundError(error) {
48
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
49
+ }
47
50
  // src/hash.ts
48
51
  function sha256Hex(input) {
49
52
  const hasher = new Bun.CryptoHasher("sha256");
@@ -52,6 +52,9 @@ class ChatPermissionError extends ChatError {
52
52
  this.name = "ChatPermissionError";
53
53
  }
54
54
  }
55
+ function isChatNotFoundError(error) {
56
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
57
+ }
55
58
  // src/hash.ts
56
59
  function sha256Hex(input) {
57
60
  const hasher = new Bun.CryptoHasher("sha256");
@@ -47,6 +47,9 @@ class ChatPermissionError extends ChatError {
47
47
  this.name = "ChatPermissionError";
48
48
  }
49
49
  }
50
+ function isChatNotFoundError(error) {
51
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
52
+ }
50
53
  // src/hash.ts
51
54
  function sha256Hex(input) {
52
55
  const hasher = new Bun.CryptoHasher("sha256");
@@ -300,6 +300,9 @@ class ChatPermissionError extends ChatError {
300
300
  this.name = "ChatPermissionError";
301
301
  }
302
302
  }
303
+ function isChatNotFoundError(error) {
304
+ return typeof error === "object" && error !== null && error.name === "ChatNotFoundError" && error.code === "not_found" && typeof error.resource === "string" && typeof error.resourceId === "string";
305
+ }
303
306
  // src/hash.ts
304
307
  function sha256Hex(input) {
305
308
  const hasher = new Bun.CryptoHasher("sha256");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khoralabs/chat",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Signed chat ledger: service, persistence backends, and HTTP transport",
5
5
  "license": "MIT",
6
6
  "type": "module",