@khoralabs/chat 0.1.0
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 +7 -0
- package/dist/domain.d.ts +10 -0
- package/dist/domain.d.ts.map +1 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/events.d.ts +59 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/hash.d.ts +47 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/http/client.d.ts +21 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +367 -0
- package/dist/http/config.d.ts +5 -0
- package/dist/http/config.d.ts.map +1 -0
- package/dist/http/index.d.ts +6 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +1084 -0
- package/dist/http/routes.d.ts +15 -0
- package/dist/http/routes.d.ts.map +1 -0
- package/dist/http/routes.js +645 -0
- package/dist/http/server.d.ts +19 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +973 -0
- package/dist/http/service.d.ts +34 -0
- package/dist/http/service.d.ts.map +1 -0
- package/dist/http/service.js +501 -0
- package/dist/ids.d.ts +8 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +453 -0
- package/dist/lineage.d.ts +5 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/persistence/core/index.d.ts +2 -0
- package/dist/persistence/core/index.d.ts.map +1 -0
- package/dist/persistence/core/persistence/base-persistence.d.ts +12 -0
- package/dist/persistence/core/persistence/base-persistence.d.ts.map +1 -0
- package/dist/persistence/core/persistence/helpers.d.ts +30 -0
- package/dist/persistence/core/persistence/helpers.d.ts.map +1 -0
- package/dist/persistence/core/persistence/index.d.ts +6 -0
- package/dist/persistence/core/persistence/index.d.ts.map +1 -0
- package/dist/persistence/core/persistence/index.js +1276 -0
- package/dist/persistence/core/persistence/memory-persistence.d.ts +55 -0
- package/dist/persistence/core/persistence/memory-persistence.d.ts.map +1 -0
- package/dist/persistence/core/persistence/signed-persistence.d.ts +18 -0
- package/dist/persistence/core/persistence/signed-persistence.d.ts.map +1 -0
- package/dist/persistence/core/persistence/types.d.ts +210 -0
- package/dist/persistence/core/persistence/types.d.ts.map +1 -0
- package/dist/persistence/sqlite/index.d.ts +3 -0
- package/dist/persistence/sqlite/index.d.ts.map +1 -0
- package/dist/persistence/sqlite/index.js +1544 -0
- package/dist/persistence/sqlite/persistence/persistence.d.ts +49 -0
- package/dist/persistence/sqlite/persistence/persistence.d.ts.map +1 -0
- package/dist/persistence/sqlite/persistence/schema.d.ts +4 -0
- package/dist/persistence/sqlite/persistence/schema.d.ts.map +1 -0
- package/dist/persistence/sqlite/persistence/streaming.d.ts +27 -0
- package/dist/persistence/sqlite/persistence/streaming.d.ts.map +1 -0
- package/dist/persistence/testing/index.d.ts +2 -0
- package/dist/persistence/testing/index.d.ts.map +1 -0
- package/dist/persistence/testing/index.js +689 -0
- package/dist/persistence/testing/persistence/contract.d.ts +3 -0
- package/dist/persistence/testing/persistence/contract.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/index.d.ts +10 -0
- package/dist/persistence/turso-serverless/index.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/index.js +1812 -0
- package/dist/persistence/turso-serverless/persistence/local-sqlite.d.ts +5 -0
- package/dist/persistence/turso-serverless/persistence/local-sqlite.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/persistence/persistence.d.ts +49 -0
- package/dist/persistence/turso-serverless/persistence/persistence.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/persistence/schema.d.ts +4 -0
- package/dist/persistence/turso-serverless/persistence/schema.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/persistence/sql.d.ts +20 -0
- package/dist/persistence/turso-serverless/persistence/sql.d.ts.map +1 -0
- package/dist/persistence/turso-serverless/persistence/sql.js +53 -0
- package/dist/persistence/turso-serverless/persistence/streaming.d.ts +27 -0
- package/dist/persistence/turso-serverless/persistence/streaming.d.ts.map +1 -0
- package/dist/service.d.ts +49 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/stream.d.ts +32 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/types.d.ts +201 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validation.d.ts +4 -0
- package/dist/validation.d.ts.map +1 -0
- package/package.json +104 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# `@khoralabs/chat`
|
|
2
|
+
|
|
3
|
+
Signed chat ledger: domain service, persistence backends, and HTTP/WS transport.
|
|
4
|
+
|
|
5
|
+
Requires **Bun** for hashing, `./sqlite`, and `./http/server`. Use `./turso-serverless` for Turso/libSQL-style SQL.
|
|
6
|
+
|
|
7
|
+
See the [repo README](../../README.md) for the full entrypoint map.
|
package/dist/domain.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./errors.js";
|
|
2
|
+
export * from "./events.js";
|
|
3
|
+
export * from "./hash.js";
|
|
4
|
+
export * from "./ids.js";
|
|
5
|
+
export * from "./lineage.js";
|
|
6
|
+
export type * from "./persistence/core/persistence/types.js";
|
|
7
|
+
export * from "./stream.js";
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
export * from "./validation.js";
|
|
10
|
+
//# sourceMappingURL=domain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,mBAAmB,yCAAyC,CAAC;AAC7D,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class ChatError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(code: string, message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ChatNotFoundError extends ChatError {
|
|
6
|
+
readonly resource: string;
|
|
7
|
+
readonly resourceId: string;
|
|
8
|
+
constructor(resource: string, resourceId: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class ChatConflictError extends ChatError {
|
|
11
|
+
readonly reason: "head_conflict" | "idempotency_mismatch" | "stream_revision_conflict";
|
|
12
|
+
constructor(reason: "head_conflict" | "idempotency_mismatch" | "stream_revision_conflict", message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class ChatValidationError extends ChatError {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class ChatPermissionError extends ChatError {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Channel, Post, ScopeRef, Thread, ThreadHead } from "./types.js";
|
|
2
|
+
export type PostAppended = {
|
|
3
|
+
type: "post.appended";
|
|
4
|
+
threadId: string;
|
|
5
|
+
post: Post;
|
|
6
|
+
};
|
|
7
|
+
export type PostUpdated = {
|
|
8
|
+
type: "post.updated";
|
|
9
|
+
threadId: string;
|
|
10
|
+
post: Post;
|
|
11
|
+
};
|
|
12
|
+
export type PostDeleted = {
|
|
13
|
+
type: "post.deleted";
|
|
14
|
+
threadId: string;
|
|
15
|
+
postId: string;
|
|
16
|
+
deletedAtMs: number;
|
|
17
|
+
};
|
|
18
|
+
export type PostStreamStarted = {
|
|
19
|
+
type: "post.stream.started";
|
|
20
|
+
threadId: string;
|
|
21
|
+
post: Post;
|
|
22
|
+
revision: number;
|
|
23
|
+
};
|
|
24
|
+
export type PostStreamDelta = {
|
|
25
|
+
type: "post.stream.delta";
|
|
26
|
+
threadId: string;
|
|
27
|
+
post: Post;
|
|
28
|
+
revision: number;
|
|
29
|
+
};
|
|
30
|
+
export type PostStreamCompleted = {
|
|
31
|
+
type: "post.stream.completed";
|
|
32
|
+
threadId: string;
|
|
33
|
+
post: Post;
|
|
34
|
+
head: ThreadHead;
|
|
35
|
+
revision: number;
|
|
36
|
+
};
|
|
37
|
+
export type PostStreamAborted = {
|
|
38
|
+
type: "post.stream.aborted";
|
|
39
|
+
threadId: string;
|
|
40
|
+
postId: string;
|
|
41
|
+
revision: number;
|
|
42
|
+
deletedAtMs: number;
|
|
43
|
+
};
|
|
44
|
+
export type ThreadCreated = {
|
|
45
|
+
type: "thread.created";
|
|
46
|
+
thread: Thread;
|
|
47
|
+
};
|
|
48
|
+
export type ParticipantAdded = {
|
|
49
|
+
type: "participant.added";
|
|
50
|
+
threadId: string;
|
|
51
|
+
scope: ScopeRef;
|
|
52
|
+
role: string;
|
|
53
|
+
};
|
|
54
|
+
export type ChannelCreated = {
|
|
55
|
+
type: "channel.created";
|
|
56
|
+
channel: Channel;
|
|
57
|
+
};
|
|
58
|
+
export type ChatEvent = PostAppended | PostUpdated | PostDeleted | PostStreamStarted | PostStreamDelta | PostStreamCompleted | PostStreamAborted | ThreadCreated | ParticipantAdded | ChannelCreated;
|
|
59
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GACjB,YAAY,GACZ,WAAW,GACX,WAAW,GACX,iBAAiB,GACjB,eAAe,GACf,mBAAmB,GACnB,iBAAiB,GACjB,aAAa,GACb,gBAAgB,GAChB,cAAc,CAAC"}
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Mention, ScopeRef, SignedEnvelope } from "./types.js";
|
|
2
|
+
export declare function sha256Hex(input: string | Uint8Array): string;
|
|
3
|
+
export type CanonicalPostVersionPayload = {
|
|
4
|
+
postId: string;
|
|
5
|
+
versionId: string;
|
|
6
|
+
threadId: string;
|
|
7
|
+
author: ScopeRef;
|
|
8
|
+
role: string;
|
|
9
|
+
parts: unknown[];
|
|
10
|
+
metadata?: unknown;
|
|
11
|
+
mentions?: Mention[];
|
|
12
|
+
model?: unknown;
|
|
13
|
+
usage?: unknown;
|
|
14
|
+
parentVersionId?: string | null;
|
|
15
|
+
previousPostVersionId?: string | null;
|
|
16
|
+
};
|
|
17
|
+
export declare function canonicalPostVersionPayload(input: CanonicalPostVersionPayload): string;
|
|
18
|
+
export declare function computeContentHash(payload: CanonicalPostVersionPayload): string;
|
|
19
|
+
export declare function computeLineageHash(input: {
|
|
20
|
+
previousLineageHash: string | null;
|
|
21
|
+
contentHash: string;
|
|
22
|
+
postId: string;
|
|
23
|
+
versionId: string;
|
|
24
|
+
}): string;
|
|
25
|
+
export type CanonicalAclEventPayload = {
|
|
26
|
+
type: string;
|
|
27
|
+
targetType: "channel" | "thread";
|
|
28
|
+
targetId: string;
|
|
29
|
+
scope?: ScopeRef;
|
|
30
|
+
role?: string;
|
|
31
|
+
actor: ScopeRef;
|
|
32
|
+
previousAclEventId?: string | null;
|
|
33
|
+
};
|
|
34
|
+
export declare function canonicalAclEventPayload(input: CanonicalAclEventPayload): string;
|
|
35
|
+
export declare function computeAclEventContentHash(payload: CanonicalAclEventPayload): string;
|
|
36
|
+
export declare function signedPayloadBytes(payload: string): Uint8Array;
|
|
37
|
+
export type SignablePostVersion = CanonicalPostVersionPayload & {
|
|
38
|
+
contentHash: string;
|
|
39
|
+
lineageHash: string;
|
|
40
|
+
};
|
|
41
|
+
export declare function canonicalSignedPostVersionPayload(input: SignablePostVersion): string;
|
|
42
|
+
export declare function canonicalSignedAclEventPayload(input: {
|
|
43
|
+
contentHash: string;
|
|
44
|
+
payload: CanonicalAclEventPayload;
|
|
45
|
+
}): string;
|
|
46
|
+
export declare function maybeSignPayload(payload: string, signer: ScopeRef, sign: (payload: Uint8Array, signer: ScopeRef) => Promise<SignedEnvelope>): Promise<SignedEnvelope | undefined>;
|
|
47
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEpE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAI5D;AAiBD,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,2BAA2B,GAAG,MAAM,CAetF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM,CAE/E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAST;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,wBAAwB,GAAG,MAAM,CAUhF;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAEpF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAE9D;AAED,MAAM,MAAM,mBAAmB,GAAG,2BAA2B,GAAG;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAMpF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,wBAAwB,CAAC;CACnC,GAAG,MAAM,CAKT;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,QAAQ,EAChB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC,cAAc,CAAC,GACvE,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAErC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UIMessage } from "ai";
|
|
2
|
+
import type { ChatEvent, StartStreamedPostInput } from "../domain.js";
|
|
3
|
+
import type { ChatService } from "../service.js";
|
|
4
|
+
export type ChatFetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
export type ChatServiceClientOptions = {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
token: string;
|
|
8
|
+
fetchFn?: ChatFetchFn;
|
|
9
|
+
subscribeToThread?: (threadId: string, handler: (event: ChatEvent) => void) => () => void;
|
|
10
|
+
};
|
|
11
|
+
export type ChatServiceClient = Pick<ChatService, "getChannel" | "createChannel" | "getThread" | "createThread" | "listPosts" | "listThreads" | "appendPost" | "getThreadTip" | "listThreadParticipants" | "addThreadParticipant" | "setPostVersionSignature" | "startStreamedPost" | "applyPostDelta" | "completeStreamedPost" | "abortStreamedPost"> & {
|
|
12
|
+
subscribeToThread(threadId: string, handler: (event: ChatEvent) => void): () => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function createChatClient(options: ChatServiceClientOptions): ChatServiceClient;
|
|
15
|
+
export type StartStreamedPostBody = {
|
|
16
|
+
author: StartStreamedPostInput["author"];
|
|
17
|
+
message: UIMessage;
|
|
18
|
+
threadId: string;
|
|
19
|
+
idempotencyKey?: string;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/http/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAGV,SAAS,EAMT,sBAAsB,EACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAuBjD,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEnG,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CAC3F,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,WAAW,EACT,YAAY,GACZ,eAAe,GACf,WAAW,GACX,cAAc,GACd,WAAW,GACX,aAAa,GACb,YAAY,GACZ,cAAc,GACd,wBAAwB,GACxB,sBAAsB,GACtB,yBAAyB,GACzB,mBAAmB,GACnB,gBAAgB,GAChB,sBAAsB,GACtB,mBAAmB,CACtB,GAAG;IACF,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACtF,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CA+ErF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
class ChatError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "ChatError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class ChatNotFoundError extends ChatError {
|
|
15
|
+
resource;
|
|
16
|
+
resourceId;
|
|
17
|
+
constructor(resource, resourceId) {
|
|
18
|
+
super("not_found", `${resource} not found: ${resourceId}`);
|
|
19
|
+
this.name = "ChatNotFoundError";
|
|
20
|
+
this.resource = resource;
|
|
21
|
+
this.resourceId = resourceId;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class ChatConflictError extends ChatError {
|
|
26
|
+
reason;
|
|
27
|
+
constructor(reason, message) {
|
|
28
|
+
super("conflict", message);
|
|
29
|
+
this.name = "ChatConflictError";
|
|
30
|
+
this.reason = reason;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class ChatValidationError extends ChatError {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super("validation", message);
|
|
37
|
+
this.name = "ChatValidationError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class ChatPermissionError extends ChatError {
|
|
42
|
+
constructor(message) {
|
|
43
|
+
super("permission_denied", message);
|
|
44
|
+
this.name = "ChatPermissionError";
|
|
45
|
+
}
|
|
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
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// src/http/client.ts
|
|
270
|
+
async function readJson(res) {
|
|
271
|
+
const text = await res.text();
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
let message = `Chat request failed ${res.status}`;
|
|
274
|
+
try {
|
|
275
|
+
const data = JSON.parse(text);
|
|
276
|
+
if (data.error !== undefined && data.error.length > 0)
|
|
277
|
+
message = data.error;
|
|
278
|
+
} catch {
|
|
279
|
+
if (text.length > 0)
|
|
280
|
+
message = text;
|
|
281
|
+
}
|
|
282
|
+
if (res.status === 404) {
|
|
283
|
+
const match = /^(channel|thread|post) not found: (.+)$/i.exec(message);
|
|
284
|
+
if (match?.[1] !== undefined && match[2] !== undefined) {
|
|
285
|
+
throw new ChatNotFoundError(match[1].toLowerCase(), match[2]);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
throw new Error(message);
|
|
289
|
+
}
|
|
290
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
291
|
+
}
|
|
292
|
+
function createChatClient(options) {
|
|
293
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
294
|
+
const fetchFn = options.fetchFn ?? fetch;
|
|
295
|
+
async function post(path, body) {
|
|
296
|
+
const res = await fetchFn(`${baseUrl}${path}`, {
|
|
297
|
+
method: "POST",
|
|
298
|
+
headers: {
|
|
299
|
+
Authorization: `Bearer ${options.token}`,
|
|
300
|
+
"Content-Type": "application/json"
|
|
301
|
+
},
|
|
302
|
+
body: JSON.stringify(body)
|
|
303
|
+
});
|
|
304
|
+
return readJson(res);
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
getChannel(id) {
|
|
308
|
+
return post("/channels/get", { channelId: id });
|
|
309
|
+
},
|
|
310
|
+
createChannel(input) {
|
|
311
|
+
return post("/channels/create", input);
|
|
312
|
+
},
|
|
313
|
+
getThread(id) {
|
|
314
|
+
return post("/threads/get", { threadId: id });
|
|
315
|
+
},
|
|
316
|
+
createThread(input) {
|
|
317
|
+
return post("/threads/create", input);
|
|
318
|
+
},
|
|
319
|
+
listThreads(input) {
|
|
320
|
+
return post("/threads/list", input);
|
|
321
|
+
},
|
|
322
|
+
listPosts(input) {
|
|
323
|
+
return post("/threads/list-posts", input);
|
|
324
|
+
},
|
|
325
|
+
async appendPost(input) {
|
|
326
|
+
return post("/threads/append-post", input);
|
|
327
|
+
},
|
|
328
|
+
async getThreadTip(threadId) {
|
|
329
|
+
const result = await post("/threads/tip", { threadId });
|
|
330
|
+
return result.tip;
|
|
331
|
+
},
|
|
332
|
+
async listThreadParticipants(threadId) {
|
|
333
|
+
const result = await post("/threads/list-participants", { threadId });
|
|
334
|
+
return result.participants;
|
|
335
|
+
},
|
|
336
|
+
addThreadParticipant(input) {
|
|
337
|
+
return post("/threads/add-participant", input);
|
|
338
|
+
},
|
|
339
|
+
async setPostVersionSignature(versionId, signature) {
|
|
340
|
+
await post("/posts/set-signature", { versionId, signature });
|
|
341
|
+
},
|
|
342
|
+
startStreamedPost(input) {
|
|
343
|
+
return post("/internal/chat/streamed-posts", input);
|
|
344
|
+
},
|
|
345
|
+
applyPostDelta(input) {
|
|
346
|
+
return post(`/internal/chat/posts/${encodeURIComponent(input.postId)}/deltas`, input);
|
|
347
|
+
},
|
|
348
|
+
completeStreamedPost(input) {
|
|
349
|
+
return post(`/internal/chat/posts/${encodeURIComponent(input.postId)}/complete`, input);
|
|
350
|
+
},
|
|
351
|
+
abortStreamedPost(input) {
|
|
352
|
+
return post(`/internal/chat/posts/${encodeURIComponent(input.postId)}/abort`, input);
|
|
353
|
+
},
|
|
354
|
+
subscribeToThread(threadId, handler) {
|
|
355
|
+
if (options.subscribeToThread !== undefined) {
|
|
356
|
+
return options.subscribeToThread(threadId, handler);
|
|
357
|
+
}
|
|
358
|
+
const wsUrl = `${baseUrl.replace(/^http/, "ws")}/ws/threads/${encodeURIComponent(threadId)}?token=${encodeURIComponent(options.token)}`;
|
|
359
|
+
const ws = new WebSocket(wsUrl);
|
|
360
|
+
ws.onmessage = (event) => handler(JSON.parse(String(event.data)));
|
|
361
|
+
return () => ws.close();
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
export {
|
|
366
|
+
createChatClient
|
|
367
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/http/config.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { type ChatFetchFn, type ChatServiceClient, type ChatServiceClientOptions, createChatClient, type StartStreamedPostBody, } from "./client.js";
|
|
2
|
+
export { chatHttpPort, chatInternalToken, resolveChatDataDir, resolveChatDbPath, } from "./config.js";
|
|
3
|
+
export { type AuthorizeRequest, createChatRoutes, createChatRoutesWithParams, createTokenAuthorizer, dispatchChatRoute, type RouteHandler, readJson, requireInternalToken, } from "./routes.js";
|
|
4
|
+
export { type ChatHttpServerHandle, type StartChatHttpServerOptions, startChatHttpServer, } from "./server.js";
|
|
5
|
+
export { type ChatHttpRuntime, type ChatStorage, type ChatStorageConfig, type CreateChatHttpRuntimeOptions, createChatHttpRuntime, createChatStorage, isChatNotFound, } from "./service.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,gBAAgB,EAChB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,YAAY,EACjB,QAAQ,EACR,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EACjC,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,cAAc,CAAC"}
|