@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 +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/http/client.js +3 -0
- package/dist/http/index.js +5 -2
- package/dist/http/routes.js +4 -222
- package/dist/http/server.js +23 -223
- package/dist/http/service.d.ts +1 -1
- package/dist/http/service.d.ts.map +1 -1
- package/dist/http/service.js +5 -205
- package/dist/index.js +4 -0
- package/dist/persistence/core/persistence/index.js +3 -0
- package/dist/persistence/sqlite/index.js +3 -0
- package/dist/persistence/testing/index.js +3 -0
- package/dist/persistence/turso-serverless/index.js +3 -0
- package/package.json +1 -1
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
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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"}
|
package/dist/http/client.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");
|
package/dist/http/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");
|
|
@@ -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
|
|
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
|
|
965
|
+
return isChatNotFoundError(error);
|
|
963
966
|
}
|
|
964
967
|
|
|
965
968
|
// src/http/server.ts
|
package/dist/http/routes.js
CHANGED
|
@@ -44,228 +44,10 @@ class ChatPermissionError extends ChatError {
|
|
|
44
44
|
this.name = "ChatPermissionError";
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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);
|
package/dist/http/server.js
CHANGED
|
@@ -44,228 +44,10 @@ class ChatPermissionError extends ChatError {
|
|
|
44
44
|
this.name = "ChatPermissionError";
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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
|
|
665
|
+
return isChatNotFoundError(error);
|
|
866
666
|
}
|
|
867
667
|
|
|
868
668
|
// src/http/server.ts
|
package/dist/http/service.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChatEvent, ChatPersistence } from "../domain.js";
|
|
2
|
-
import { ChatNotFoundError } from "../
|
|
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,
|
|
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"}
|
package/dist/http/service.js
CHANGED
|
@@ -53,86 +53,10 @@ class ChatPermissionError extends ChatError {
|
|
|
53
53
|
this.name = "ChatPermissionError";
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
|
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");
|