@convex-dev/persistent-text-streaming 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +3 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/dataModel.d.ts +1 -1
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/lib.d.ts +7 -0
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +44 -1
- package/dist/component/lib.js.map +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +69 -85
- package/dist/react/index.js.map +1 -1
- package/package.json +15 -11
- package/src/client/index.ts +13 -0
- package/src/component/_generated/component.ts +7 -0
- package/src/component/_generated/dataModel.ts +1 -1
- package/src/component/_generated/server.ts +0 -5
- package/src/component/lib.ts +48 -1
- package/src/react/index.ts +74 -91
package/dist/client/index.d.ts
CHANGED
|
@@ -70,6 +70,14 @@ export declare class PersistentTextStreaming {
|
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
stream<A extends GenericActionCtx<GenericDataModel>>(ctx: A, request: Request, streamId: StreamId, streamWriter: StreamWriter<A>): Promise<Response>;
|
|
73
|
+
/**
|
|
74
|
+
* Delete a stream and all its chunks. Deletion happens asynchronously
|
|
75
|
+
* in batches, so this returns immediately.
|
|
76
|
+
*
|
|
77
|
+
* @param ctx - A convex context capable of running mutations.
|
|
78
|
+
* @param streamId - The ID of the stream to delete.
|
|
79
|
+
*/
|
|
80
|
+
deleteStream(ctx: RunMutationCtx, streamId: StreamId): Promise<void>;
|
|
73
81
|
private addChunk;
|
|
74
82
|
private setStreamStatus;
|
|
75
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAK,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAC;AACvD,eAAO,MAAM,iBAAiB,qDAAa,CAAC;AAC5C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAC5D,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CACvE,GAAG,EAAE,CAAC,EACN,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,aAAa,KACzB,OAAO,CAAC,IAAI,CAAC,CAAC;AAQnB,qBAAa,uBAAuB;IAEzB,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM;gBADhB,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,EAC7B,OAAO,CAAC,EAAE,MAAM,YAAA;IAGzB;;;;;;;;;;;;;;;;OAgBG;IAEG,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC;IAK1D;;;;;;;;;;;;OAYG;IACG,aAAa,CACjB,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,UAAU,CAAC;IAQtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,MAAM,CAAC,CAAC,SAAS,gBAAgB,CAAC,gBAAgB,CAAC,EACvD,GAAG,EAAE,CAAC,EACN,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
package/dist/client/index.js
CHANGED
|
@@ -128,6 +128,18 @@ export class PersistentTextStreaming {
|
|
|
128
128
|
// Send the readable back to the browser
|
|
129
129
|
return new Response(readable);
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Delete a stream and all its chunks. Deletion happens asynchronously
|
|
133
|
+
* in batches, so this returns immediately.
|
|
134
|
+
*
|
|
135
|
+
* @param ctx - A convex context capable of running mutations.
|
|
136
|
+
* @param streamId - The ID of the stream to delete.
|
|
137
|
+
*/
|
|
138
|
+
async deleteStream(ctx, streamId) {
|
|
139
|
+
await ctx.runMutation(this.component.lib.deleteStream, {
|
|
140
|
+
streamId,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
131
143
|
// Internal helper -- add a chunk to the stream.
|
|
132
144
|
async addChunk(ctx, streamId, text, final) {
|
|
133
145
|
await ctx.runMutation(this.component.lib.addChunk, {
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,9 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
26
26
|
text: string;
|
|
27
27
|
}, any, Name>;
|
|
28
28
|
createStream: FunctionReference<"mutation", "internal", {}, any, Name>;
|
|
29
|
+
deleteStream: FunctionReference<"mutation", "internal", {
|
|
30
|
+
streamId: string;
|
|
31
|
+
}, null, Name>;
|
|
29
32
|
getStreamStatus: FunctionReference<"query", "internal", {
|
|
30
33
|
streamId: string;
|
|
31
34
|
}, "pending" | "streaming" | "done" | "error" | "timeout", Name>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,IAC3E;IACE,GAAG,EAAE;QACH,QAAQ,EAAE,iBAAiB,CACzB,UAAU,EACV,UAAU,EACV;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAClD,GAAG,EACH,IAAI,CACL,CAAC;QACF,YAAY,EAAE,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACvE,eAAe,EAAE,iBAAiB,CAChC,OAAO,EACP,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,EACpB,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,EACtD,IAAI,CACL,CAAC;QACF,aAAa,EAAE,iBAAiB,CAC9B,OAAO,EACP,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,EACpB;YACE,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;YAC/D,IAAI,EAAE,MAAM,CAAC;SACd,EACD,IAAI,CACL,CAAC;QACF,eAAe,EAAE,iBAAiB,CAChC,UAAU,EACV,UAAU,EACV;YACE,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;YAC/D,QAAQ,EAAE,MAAM,CAAC;SAClB,EACD,GAAG,EACH,IAAI,CACL,CAAC;KACH,CAAC;CACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/component.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,IAC3E;IACE,GAAG,EAAE;QACH,QAAQ,EAAE,iBAAiB,CACzB,UAAU,EACV,UAAU,EACV;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAClD,GAAG,EACH,IAAI,CACL,CAAC;QACF,YAAY,EAAE,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACvE,YAAY,EAAE,iBAAiB,CAC7B,UAAU,EACV,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,EACpB,IAAI,EACJ,IAAI,CACL,CAAC;QACF,eAAe,EAAE,iBAAiB,CAChC,OAAO,EACP,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,EACpB,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,EACtD,IAAI,CACL,CAAC;QACF,aAAa,EAAE,iBAAiB,CAC9B,OAAO,EACP,UAAU,EACV;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,EACpB;YACE,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;YAC/D,IAAI,EAAE,MAAM,CAAC;SACd,EACD,IAAI,CACL,CAAC;QACF,eAAe,EAAE,iBAAiB,CAChC,UAAU,EACV,UAAU,EACV;YACE,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;YAC/D,QAAQ,EAAE,MAAM,CAAC;SAClB,EACD,GAAG,EACH,IAAI,CACL,CAAC;KACH,CAAC;CACH,CAAC"}
|
|
@@ -25,7 +25,7 @@ export type Doc<TableName extends TableNames> = DocumentByName<DataModel, TableN
|
|
|
25
25
|
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
26
26
|
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
27
27
|
*
|
|
28
|
-
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
|
28
|
+
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
|
|
29
29
|
*
|
|
30
30
|
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
31
31
|
* strings when type checking.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/component/_generated/server.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,eAAe,CAAC;AAUvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAgB,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,CACxC,CAAC;AAEvB;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,EAAE,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAmB,CAAC;AAE9E;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,CAC3C,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAiB,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CACzC,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,iBAAqC,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC"}
|
package/dist/component/lib.d.ts
CHANGED
|
@@ -17,5 +17,12 @@ export declare const getStreamText: import("convex/server").RegisteredQuery<"pub
|
|
|
17
17
|
text: string;
|
|
18
18
|
status: "pending" | "streaming" | "done" | "error" | "timeout";
|
|
19
19
|
}>>;
|
|
20
|
+
export declare const deleteStream: import("convex/server").RegisteredMutation<"public", {
|
|
21
|
+
streamId: import("convex/values").GenericId<"streams">;
|
|
22
|
+
}, Promise<void>>;
|
|
23
|
+
export declare const _deleteChunksPage: import("convex/server").RegisteredMutation<"internal", {
|
|
24
|
+
streamId: import("convex/values").GenericId<"streams">;
|
|
25
|
+
cursor: string | null;
|
|
26
|
+
}, Promise<void>>;
|
|
20
27
|
export declare const cleanupExpiredStreams: import("convex/server").RegisteredMutation<"internal", {}, Promise<void>>;
|
|
21
28
|
//# sourceMappingURL=lib.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,iHAQvB,CAAC;AAKH,eAAO,MAAM,QAAQ;;;;iBA4BnB,CAAC;AAIH,eAAO,MAAM,eAAe;;;iBA2B1B,CAAC;AAGH,eAAO,MAAM,eAAe;;mEAS1B,CAAC;AAIH,eAAO,MAAM,aAAa;;;;;GA0BxB,CAAC;AAQH,eAAO,MAAM,YAAY;;iBAgBvB,CAAC;AAGH,eAAO,MAAM,iBAAiB;;;iBAqB5B,CAAC;AAIH,eAAO,MAAM,qBAAqB,2EAsBhC,CAAC"}
|
package/dist/component/lib.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
1
2
|
import { v } from "convex/values";
|
|
3
|
+
import { internal } from "./_generated/api.js";
|
|
2
4
|
import { internalMutation, mutation, query } from "./_generated/server.js";
|
|
3
|
-
import { streamStatusValidator } from "./schema.js";
|
|
5
|
+
import schema, { streamStatusValidator } from "./schema.js";
|
|
4
6
|
// Create a new stream with zero chunks.
|
|
5
7
|
export const createStream = mutation({
|
|
6
8
|
args: {},
|
|
@@ -107,6 +109,47 @@ export const getStreamText = query({
|
|
|
107
109
|
});
|
|
108
110
|
const EXPIRATION_TIME = 20 * 60 * 1000; // 20 minutes in milliseconds
|
|
109
111
|
const BATCH_SIZE = 100;
|
|
112
|
+
const DELETE_BATCH_SIZE = 64;
|
|
113
|
+
// Delete a stream and all its chunks.
|
|
114
|
+
// The stream is deleted immediately; chunks are cleaned up asynchronously.
|
|
115
|
+
export const deleteStream = mutation({
|
|
116
|
+
args: {
|
|
117
|
+
streamId: v.id("streams"),
|
|
118
|
+
},
|
|
119
|
+
returns: v.null(),
|
|
120
|
+
handler: async (ctx, args) => {
|
|
121
|
+
const stream = await ctx.db.get(args.streamId);
|
|
122
|
+
if (!stream) {
|
|
123
|
+
throw new Error(`Stream ${args.streamId} not found`);
|
|
124
|
+
}
|
|
125
|
+
await ctx.db.delete(args.streamId);
|
|
126
|
+
await ctx.scheduler.runAfter(0, internal.lib._deleteChunksPage, {
|
|
127
|
+
streamId: args.streamId,
|
|
128
|
+
cursor: null,
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// Internal: delete a page of chunks for a deleted stream, re-scheduling if more remain.
|
|
133
|
+
export const _deleteChunksPage = internalMutation({
|
|
134
|
+
args: {
|
|
135
|
+
streamId: v.id("streams"),
|
|
136
|
+
cursor: v.union(v.string(), v.null()),
|
|
137
|
+
},
|
|
138
|
+
returns: v.null(),
|
|
139
|
+
handler: async (ctx, args) => {
|
|
140
|
+
const result = await paginator(ctx.db, schema)
|
|
141
|
+
.query("chunks")
|
|
142
|
+
.withIndex("byStream", (q) => q.eq("streamId", args.streamId))
|
|
143
|
+
.paginate({ cursor: args.cursor, numItems: DELETE_BATCH_SIZE });
|
|
144
|
+
await Promise.all(result.page.map((chunk) => ctx.db.delete(chunk._id)));
|
|
145
|
+
if (!result.isDone) {
|
|
146
|
+
await ctx.scheduler.runAfter(0, internal.lib._deleteChunksPage, {
|
|
147
|
+
streamId: args.streamId,
|
|
148
|
+
cursor: result.continueCursor,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
});
|
|
110
153
|
// If the last chunk of a stream was added more than 20 minutes ago,
|
|
111
154
|
// set the stream to timeout. The action feeding it has to be dead.
|
|
112
155
|
export const cleanupExpiredStreams = internalMutation({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,MAAM,EAAE,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE5D,wCAAwC;AACxC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;IACnC,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;YAC9C,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC,CAAC;AAEH,2BAA2B;AAC3B,4CAA4C;AAC5C,8DAA8D;AAC9D,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC/B,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;QACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAChC,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE;YAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAChC,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,8BAA8B;AAC9B,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;IACtC,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;QACzB,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EACpB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EACtB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EACjB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAClB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CACrB;KACF;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACjE,OAAO,CAAC,GAAG,CACT,qDAAqD,EACrD,MAAM,CACP,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;YAChC,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,8BAA8B;AAC9B,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;IACnC,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;KAC1B;IACD,OAAO,EAAE,qBAAqB;IAC9B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,OAAO,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC;IACnC,CAAC;CACF,CAAC,CAAC;AAEH,iCAAiC;AACjC,yCAAyC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;IACjC,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;KAC1B;IACD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,MAAM,EAAE,qBAAqB;KAC9B,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE;iBACxB,KAAK,CAAC,QAAQ,CAAC;iBACf,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;iBAC7D,OAAO,EAAE,CAAC;YACb,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,6BAA6B;AACrE,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,sCAAsC;AACtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;IACnC,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;KAC1B;IACD,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE;IACjB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,QAAQ,YAAY,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,iBAAiB,EAAE;YAC9D,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,wFAAwF;AACxF,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;IAChD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;QACzB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;KACtC;IACD,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE;IACjB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC;aAC3C,KAAK,CAAC,QAAQ,CAAC;aACf,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;aAC7D,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAElE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAExE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,iBAAiB,EAAE;gBAC9D,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,MAAM,CAAC,cAAc;aAC9B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,oEAAoE;AACpE,mEAAmE;AACnE,MAAM,CAAC,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;IACpD,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,EAAE;aAChC,KAAK,CAAC,SAAS,CAAC;aAChB,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;aACvD,IAAI,CAAC,UAAU,CAAC,CAAC;QACpB,MAAM,gBAAgB,GAAG,MAAM,GAAG,CAAC,EAAE;aAClC,KAAK,CAAC,SAAS,CAAC;aAChB,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;aACzD,IAAI,CAAC,UAAU,CAAC,CAAC;QAEpB,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,cAAc,EAAE,GAAG,gBAAgB,CAAC,EAAE,CAAC;YAC9D,IAAI,GAAG,GAAG,MAAM,CAAC,aAAa,GAAG,eAAe,EAAE,CAAC;gBACjD,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;gBACtD,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;oBAC7B,MAAM,EAAE,SAAS;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CACvB,iBAAiB,EAAE,iBAAiB,CAClC,OAAO,EACP,QAAQ,EACR;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EACpB,UAAU,CACX,EACD,SAAS,EAAE,GAAG,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,IAAI,CAAC,EAAE;IAEL,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CACvB,iBAAiB,EAAE,iBAAiB,CAClC,OAAO,EACP,QAAQ,EACR;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EACpB,UAAU,CACX,EACD,SAAS,EAAE,GAAG,EACd,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,IAAI,CAAC,EAAE;IAEL,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,cAuHF"}
|
package/dist/react/index.js
CHANGED
|
@@ -19,9 +19,11 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
19
19
|
* @returns The body and status of the stream.
|
|
20
20
|
*/
|
|
21
21
|
export function useStream(getPersistentBody, streamUrl, driven, streamId, opts) {
|
|
22
|
+
const [streamBody, setStreamBody] = useState("");
|
|
22
23
|
const [streamEnded, setStreamEnded] = useState(null);
|
|
23
|
-
//
|
|
24
|
-
|
|
24
|
+
// Track the active streamId to handle multiple streams and serve as a
|
|
25
|
+
// Strict Mode guard (prevents double-firing when the same streamId is seen).
|
|
26
|
+
const activeStreamRef = useRef(undefined);
|
|
25
27
|
const usePersistence = useMemo(() => {
|
|
26
28
|
// Something is wrong with the stream, so we need to use the database value.
|
|
27
29
|
if (streamEnded === false) {
|
|
@@ -34,43 +36,75 @@ export function useStream(getPersistentBody, streamUrl, driven, streamId, opts)
|
|
|
34
36
|
// Otherwise, we'll try to drive the stream and use the HTTP response.
|
|
35
37
|
return false;
|
|
36
38
|
}, [driven, streamEnded]);
|
|
37
|
-
// console.log("usePersistence", usePersistence);
|
|
38
39
|
const persistentBody = useQuery(getPersistentBody, usePersistence && streamId ? { streamId } : "skip");
|
|
39
|
-
const [streamBody, setStreamBody] = useState("");
|
|
40
40
|
useEffect(() => {
|
|
41
|
-
if (driven
|
|
42
|
-
|
|
43
|
-
void (async () => {
|
|
44
|
-
const success = await startStreaming(streamUrl, streamId, (text) => {
|
|
45
|
-
setStreamBody((prev) => prev + text);
|
|
46
|
-
}, {
|
|
47
|
-
...opts?.headers,
|
|
48
|
-
...(opts?.authToken
|
|
49
|
-
? { Authorization: `Bearer ${opts.authToken}` }
|
|
50
|
-
: {}),
|
|
51
|
-
});
|
|
52
|
-
setStreamEnded(success);
|
|
53
|
-
})();
|
|
54
|
-
// If we get remounted, we don't want to start a new stream.
|
|
55
|
-
return () => {
|
|
56
|
-
streamStarted.current = true;
|
|
57
|
-
};
|
|
41
|
+
if (!driven || !streamId) {
|
|
42
|
+
return;
|
|
58
43
|
}
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
streamId
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
44
|
+
// Strict Mode guard: don't restart streaming for the same streamId
|
|
45
|
+
if (streamId === activeStreamRef.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// New stream: reset state and track the new streamId
|
|
49
|
+
activeStreamRef.current = streamId;
|
|
50
|
+
setStreamBody("");
|
|
51
|
+
setStreamEnded(null);
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
void (async () => {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(streamUrl, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: JSON.stringify({ streamId }),
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
...opts?.headers,
|
|
61
|
+
...(opts?.authToken
|
|
62
|
+
? { Authorization: `Bearer ${opts.authToken}` }
|
|
63
|
+
: {}),
|
|
64
|
+
},
|
|
65
|
+
signal: controller.signal,
|
|
66
|
+
});
|
|
67
|
+
if (response.status === 205) {
|
|
68
|
+
console.error("Stream already finished", response);
|
|
69
|
+
setStreamEnded(false);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
console.error("Failed to reach streaming endpoint", response);
|
|
74
|
+
setStreamEnded(false);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!response.body) {
|
|
78
|
+
console.error("No body in response", response);
|
|
79
|
+
setStreamEnded(false);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const reader = response.body.getReader();
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
for (;;) {
|
|
85
|
+
const { done, value } = await reader.read();
|
|
86
|
+
const text = decoder.decode(value, { stream: !done });
|
|
87
|
+
if (text) {
|
|
88
|
+
setStreamBody((prev) => prev + text);
|
|
89
|
+
}
|
|
90
|
+
if (done) {
|
|
91
|
+
setStreamEnded(true);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
if (!controller.signal.aborted) {
|
|
98
|
+
console.error("Error reading stream", e);
|
|
99
|
+
setStreamEnded(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})();
|
|
103
|
+
return () => {
|
|
104
|
+
controller.abort();
|
|
105
|
+
};
|
|
106
|
+
}, [driven, streamId, streamUrl, opts?.authToken, opts?.headers]);
|
|
68
107
|
const body = useMemo(() => {
|
|
69
|
-
// console.log(
|
|
70
|
-
// "body info p vs. s",
|
|
71
|
-
// persistentBody?.text?.length ?? 0,
|
|
72
|
-
// streamBody.length
|
|
73
|
-
//);
|
|
74
108
|
if (persistentBody) {
|
|
75
109
|
return persistentBody;
|
|
76
110
|
}
|
|
@@ -88,54 +122,4 @@ export function useStream(getPersistentBody, streamUrl, driven, streamId, opts)
|
|
|
88
122
|
}, [persistentBody, streamBody, streamEnded]);
|
|
89
123
|
return body;
|
|
90
124
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Internal helper for starting a stream.
|
|
93
|
-
*
|
|
94
|
-
* @param url - The URL of the http action that will kick off the stream
|
|
95
|
-
* generation and stream the result back to the client using the component's
|
|
96
|
-
* `stream` method.
|
|
97
|
-
* @param streamId - The ID of the stream.
|
|
98
|
-
* @param onUpdate - A function that updates the stream body.
|
|
99
|
-
* @returns A promise that resolves to a boolean indicating whether the stream
|
|
100
|
-
* was started successfully. It can fail if the http action is not found, or
|
|
101
|
-
* CORS fails, or an exception is raised, or the stream is already running
|
|
102
|
-
* or finished, etc.
|
|
103
|
-
*/
|
|
104
|
-
async function startStreaming(url, streamId, onUpdate, headers) {
|
|
105
|
-
const response = await fetch(url, {
|
|
106
|
-
method: "POST",
|
|
107
|
-
body: JSON.stringify({
|
|
108
|
-
streamId: streamId,
|
|
109
|
-
}),
|
|
110
|
-
headers: { "Content-Type": "application/json", ...headers },
|
|
111
|
-
});
|
|
112
|
-
// Adapted from https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
|
|
113
|
-
if (response.status === 205) {
|
|
114
|
-
console.error("Stream already finished", response);
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (!response.ok) {
|
|
118
|
-
console.error("Failed to reach streaming endpoint", response);
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
if (!response.body) {
|
|
122
|
-
console.error("No body in response", response);
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
const reader = response.body.getReader();
|
|
126
|
-
while (true) {
|
|
127
|
-
try {
|
|
128
|
-
const { done, value } = await reader.read();
|
|
129
|
-
if (done) {
|
|
130
|
-
onUpdate(new TextDecoder().decode(value));
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
onUpdate(new TextDecoder().decode(value));
|
|
134
|
-
}
|
|
135
|
-
catch (e) {
|
|
136
|
-
console.error("Error reading stream", e);
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
125
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAIb,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAG7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CACvB,iBAKC,EACD,SAAc,EACd,MAAe,EACf,QAA8B,EAC9B,IAKC;IAED,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"email": "support@convex.dev",
|
|
8
8
|
"url": "https://github.com/get-convex/persistent-text-streaming/issues"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.3.
|
|
10
|
+
"version": "0.3.2",
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"convex",
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
"dev": "run-p -r 'dev:*'",
|
|
19
19
|
"dev:backend": "convex dev --typecheck-components",
|
|
20
20
|
"dev:frontend": "cd example && vite --clearScreen false",
|
|
21
|
-
"dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c '
|
|
22
|
-
"predev": "npm run
|
|
23
|
-
"clean": "rm -rf dist *.tsbuildinfo",
|
|
21
|
+
"dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build:codegen' --initial",
|
|
22
|
+
"predev": "convex init && npm run build",
|
|
24
23
|
"build": "tsc --project ./tsconfig.build.json",
|
|
24
|
+
"build:codegen": "npx convex codegen --component-dir ./src/component && npm run build",
|
|
25
|
+
"build:clean": "rm -rf dist *.tsbuildinfo && npm run build:codegen",
|
|
25
26
|
"typecheck": "tsc --noEmit && tsc -p example && tsc -p example/convex",
|
|
26
27
|
"lint": "eslint .",
|
|
27
28
|
"all": "run-p -r 'dev:*' 'test:watch'",
|
|
@@ -29,10 +30,10 @@
|
|
|
29
30
|
"test:watch": "vitest --typecheck --clearScreen false",
|
|
30
31
|
"test:debug": "vitest --inspect-brk --no-file-parallelism",
|
|
31
32
|
"test:coverage": "vitest run --coverage --coverage.reporter=text",
|
|
32
|
-
"
|
|
33
|
-
"alpha": "npm
|
|
34
|
-
"release": "npm
|
|
35
|
-
"version": "
|
|
33
|
+
"preversion": "npm ci && npm run build:clean && run-p test lint typecheck",
|
|
34
|
+
"alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags",
|
|
35
|
+
"release": "npm version patch && npm publish && git push --follow-tags",
|
|
36
|
+
"version": "(npm whoami || npm login) && vim -c 'normal o' -c 'normal o## '$npm_package_version CHANGELOG.md && prettier -w CHANGELOG.md && git add CHANGELOG.md"
|
|
36
37
|
},
|
|
37
38
|
"files": [
|
|
38
39
|
"dist",
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
}
|
|
63
64
|
},
|
|
64
65
|
"peerDependencies": {
|
|
65
|
-
"convex": "^1.
|
|
66
|
+
"convex": "^1.32.0",
|
|
66
67
|
"react": "~18.3.1 || ^19.0.0",
|
|
67
68
|
"react-dom": "~18.3.1 || ^19.0.0"
|
|
68
69
|
},
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"@vitejs/plugin-react": "5.1.0",
|
|
78
79
|
"chokidar-cli": "3.0.0",
|
|
79
80
|
"clsx": "2.1.1",
|
|
80
|
-
"convex": "1.
|
|
81
|
+
"convex": "1.34.1",
|
|
81
82
|
"convex-test": "0.0.38",
|
|
82
83
|
"eslint": "9.39.1",
|
|
83
84
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
@@ -97,5 +98,8 @@
|
|
|
97
98
|
"vitest": "3.2.4"
|
|
98
99
|
},
|
|
99
100
|
"types": "./dist/client/index.d.ts",
|
|
100
|
-
"module": "./dist/client/index.js"
|
|
101
|
+
"module": "./dist/client/index.js",
|
|
102
|
+
"dependencies": {
|
|
103
|
+
"convex-helpers": "^0.1.114"
|
|
104
|
+
}
|
|
101
105
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -173,6 +173,19 @@ export class PersistentTextStreaming {
|
|
|
173
173
|
return new Response(readable);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Delete a stream and all its chunks. Deletion happens asynchronously
|
|
178
|
+
* in batches, so this returns immediately.
|
|
179
|
+
*
|
|
180
|
+
* @param ctx - A convex context capable of running mutations.
|
|
181
|
+
* @param streamId - The ID of the stream to delete.
|
|
182
|
+
*/
|
|
183
|
+
async deleteStream(ctx: RunMutationCtx, streamId: StreamId): Promise<void> {
|
|
184
|
+
await ctx.runMutation(this.component.lib.deleteStream, {
|
|
185
|
+
streamId,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
176
189
|
// Internal helper -- add a chunk to the stream.
|
|
177
190
|
private async addChunk(
|
|
178
191
|
ctx: RunMutationCtx,
|
|
@@ -32,6 +32,13 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
|
|
|
32
32
|
Name
|
|
33
33
|
>;
|
|
34
34
|
createStream: FunctionReference<"mutation", "internal", {}, any, Name>;
|
|
35
|
+
deleteStream: FunctionReference<
|
|
36
|
+
"mutation",
|
|
37
|
+
"internal",
|
|
38
|
+
{ streamId: string },
|
|
39
|
+
null,
|
|
40
|
+
Name
|
|
41
|
+
>;
|
|
35
42
|
getStreamStatus: FunctionReference<
|
|
36
43
|
"query",
|
|
37
44
|
"internal",
|
|
@@ -38,7 +38,7 @@ export type Doc<TableName extends TableNames> = DocumentByName<
|
|
|
38
38
|
* Convex documents are uniquely identified by their `Id`, which is accessible
|
|
39
39
|
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
|
40
40
|
*
|
|
41
|
-
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
|
41
|
+
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
|
|
42
42
|
*
|
|
43
43
|
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
|
44
44
|
* strings when type checking.
|
|
@@ -107,11 +107,6 @@ export const internalAction: ActionBuilder<DataModel, "internal"> =
|
|
|
107
107
|
*/
|
|
108
108
|
export const httpAction: HttpActionBuilder = httpActionGeneric;
|
|
109
109
|
|
|
110
|
-
type GenericCtx =
|
|
111
|
-
| GenericActionCtx<DataModel>
|
|
112
|
-
| GenericMutationCtx<DataModel>
|
|
113
|
-
| GenericQueryCtx<DataModel>;
|
|
114
|
-
|
|
115
110
|
/**
|
|
116
111
|
* A set of services for use within Convex query functions.
|
|
117
112
|
*
|
package/src/component/lib.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { paginator } from "convex-helpers/server/pagination";
|
|
1
2
|
import { v } from "convex/values";
|
|
3
|
+
import { internal } from "./_generated/api.js";
|
|
2
4
|
import { internalMutation, mutation, query } from "./_generated/server.js";
|
|
3
|
-
import { streamStatusValidator } from "./schema.js";
|
|
5
|
+
import schema, { streamStatusValidator } from "./schema.js";
|
|
4
6
|
|
|
5
7
|
// Create a new stream with zero chunks.
|
|
6
8
|
export const createStream = mutation({
|
|
@@ -121,6 +123,51 @@ export const getStreamText = query({
|
|
|
121
123
|
|
|
122
124
|
const EXPIRATION_TIME = 20 * 60 * 1000; // 20 minutes in milliseconds
|
|
123
125
|
const BATCH_SIZE = 100;
|
|
126
|
+
const DELETE_BATCH_SIZE = 64;
|
|
127
|
+
|
|
128
|
+
// Delete a stream and all its chunks.
|
|
129
|
+
// The stream is deleted immediately; chunks are cleaned up asynchronously.
|
|
130
|
+
export const deleteStream = mutation({
|
|
131
|
+
args: {
|
|
132
|
+
streamId: v.id("streams"),
|
|
133
|
+
},
|
|
134
|
+
returns: v.null(),
|
|
135
|
+
handler: async (ctx, args) => {
|
|
136
|
+
const stream = await ctx.db.get(args.streamId);
|
|
137
|
+
if (!stream) {
|
|
138
|
+
throw new Error(`Stream ${args.streamId} not found`);
|
|
139
|
+
}
|
|
140
|
+
await ctx.db.delete(args.streamId);
|
|
141
|
+
await ctx.scheduler.runAfter(0, internal.lib._deleteChunksPage, {
|
|
142
|
+
streamId: args.streamId,
|
|
143
|
+
cursor: null,
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Internal: delete a page of chunks for a deleted stream, re-scheduling if more remain.
|
|
149
|
+
export const _deleteChunksPage = internalMutation({
|
|
150
|
+
args: {
|
|
151
|
+
streamId: v.id("streams"),
|
|
152
|
+
cursor: v.union(v.string(), v.null()),
|
|
153
|
+
},
|
|
154
|
+
returns: v.null(),
|
|
155
|
+
handler: async (ctx, args) => {
|
|
156
|
+
const result = await paginator(ctx.db, schema)
|
|
157
|
+
.query("chunks")
|
|
158
|
+
.withIndex("byStream", (q) => q.eq("streamId", args.streamId))
|
|
159
|
+
.paginate({ cursor: args.cursor, numItems: DELETE_BATCH_SIZE });
|
|
160
|
+
|
|
161
|
+
await Promise.all(result.page.map((chunk) => ctx.db.delete(chunk._id)));
|
|
162
|
+
|
|
163
|
+
if (!result.isDone) {
|
|
164
|
+
await ctx.scheduler.runAfter(0, internal.lib._deleteChunksPage, {
|
|
165
|
+
streamId: args.streamId,
|
|
166
|
+
cursor: result.continueCursor,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
124
171
|
|
|
125
172
|
// If the last chunk of a stream was added more than 20 minutes ago,
|
|
126
173
|
// set the stream to timeout. The action feeding it has to be dead.
|
package/src/react/index.ts
CHANGED
|
@@ -41,10 +41,12 @@ export function useStream(
|
|
|
41
41
|
headers?: Record<string, string>;
|
|
42
42
|
},
|
|
43
43
|
) {
|
|
44
|
-
const [
|
|
44
|
+
const [streamBody, setStreamBody] = useState<string>("");
|
|
45
|
+
const [streamEnded, setStreamEnded] = useState<boolean | null>(null);
|
|
45
46
|
|
|
46
|
-
//
|
|
47
|
-
|
|
47
|
+
// Track the active streamId to handle multiple streams and serve as a
|
|
48
|
+
// Strict Mode guard (prevents double-firing when the same streamId is seen).
|
|
49
|
+
const activeStreamRef = useRef<StreamId | undefined>(undefined);
|
|
48
50
|
|
|
49
51
|
const usePersistence = useMemo(() => {
|
|
50
52
|
// Something is wrong with the stream, so we need to use the database value.
|
|
@@ -58,53 +60,88 @@ export function useStream(
|
|
|
58
60
|
// Otherwise, we'll try to drive the stream and use the HTTP response.
|
|
59
61
|
return false;
|
|
60
62
|
}, [driven, streamEnded]);
|
|
61
|
-
|
|
63
|
+
|
|
62
64
|
const persistentBody = useQuery(
|
|
63
65
|
getPersistentBody,
|
|
64
66
|
usePersistence && streamId ? { streamId } : "skip",
|
|
65
67
|
);
|
|
66
|
-
const [streamBody, setStreamBody] = useState<string>("");
|
|
67
68
|
|
|
68
69
|
useEffect(() => {
|
|
69
|
-
if (driven
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
if (!driven || !streamId) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Strict Mode guard: don't restart streaming for the same streamId
|
|
75
|
+
if (streamId === activeStreamRef.current) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// New stream: reset state and track the new streamId
|
|
80
|
+
activeStreamRef.current = streamId;
|
|
81
|
+
setStreamBody("");
|
|
82
|
+
setStreamEnded(null);
|
|
83
|
+
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
|
|
86
|
+
void (async () => {
|
|
87
|
+
try {
|
|
88
|
+
const response = await fetch(streamUrl, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: JSON.stringify({ streamId }),
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json",
|
|
79
93
|
...opts?.headers,
|
|
80
94
|
...(opts?.authToken
|
|
81
95
|
? { Authorization: `Bearer ${opts.authToken}` }
|
|
82
96
|
: {}),
|
|
83
97
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
signal: controller.signal,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (response.status === 205) {
|
|
102
|
+
console.error("Stream already finished", response);
|
|
103
|
+
setStreamEnded(false);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
console.error("Failed to reach streaming endpoint", response);
|
|
108
|
+
setStreamEnded(false);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!response.body) {
|
|
112
|
+
console.error("No body in response", response);
|
|
113
|
+
setStreamEnded(false);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const reader = response.body.getReader();
|
|
118
|
+
const decoder = new TextDecoder();
|
|
119
|
+
|
|
120
|
+
for (;;) {
|
|
121
|
+
const { done, value } = await reader.read();
|
|
122
|
+
const text = decoder.decode(value, { stream: !done });
|
|
123
|
+
if (text) {
|
|
124
|
+
setStreamBody((prev) => prev + text);
|
|
125
|
+
}
|
|
126
|
+
if (done) {
|
|
127
|
+
setStreamEnded(true);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (e) {
|
|
132
|
+
if (!controller.signal.aborted) {
|
|
133
|
+
console.error("Error reading stream", e);
|
|
134
|
+
setStreamEnded(false);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})();
|
|
138
|
+
|
|
139
|
+
return () => {
|
|
140
|
+
controller.abort();
|
|
141
|
+
};
|
|
142
|
+
}, [driven, streamId, streamUrl, opts?.authToken, opts?.headers]);
|
|
101
143
|
|
|
102
144
|
const body = useMemo<StreamBody>(() => {
|
|
103
|
-
// console.log(
|
|
104
|
-
// "body info p vs. s",
|
|
105
|
-
// persistentBody?.text?.length ?? 0,
|
|
106
|
-
// streamBody.length
|
|
107
|
-
//);
|
|
108
145
|
if (persistentBody) {
|
|
109
146
|
return persistentBody;
|
|
110
147
|
}
|
|
@@ -123,57 +160,3 @@ export function useStream(
|
|
|
123
160
|
return body;
|
|
124
161
|
}
|
|
125
162
|
|
|
126
|
-
/**
|
|
127
|
-
* Internal helper for starting a stream.
|
|
128
|
-
*
|
|
129
|
-
* @param url - The URL of the http action that will kick off the stream
|
|
130
|
-
* generation and stream the result back to the client using the component's
|
|
131
|
-
* `stream` method.
|
|
132
|
-
* @param streamId - The ID of the stream.
|
|
133
|
-
* @param onUpdate - A function that updates the stream body.
|
|
134
|
-
* @returns A promise that resolves to a boolean indicating whether the stream
|
|
135
|
-
* was started successfully. It can fail if the http action is not found, or
|
|
136
|
-
* CORS fails, or an exception is raised, or the stream is already running
|
|
137
|
-
* or finished, etc.
|
|
138
|
-
*/
|
|
139
|
-
async function startStreaming(
|
|
140
|
-
url: URL,
|
|
141
|
-
streamId: StreamId,
|
|
142
|
-
onUpdate: (text: string) => void,
|
|
143
|
-
headers: Record<string, string>,
|
|
144
|
-
) {
|
|
145
|
-
const response = await fetch(url, {
|
|
146
|
-
method: "POST",
|
|
147
|
-
body: JSON.stringify({
|
|
148
|
-
streamId: streamId,
|
|
149
|
-
}),
|
|
150
|
-
headers: { "Content-Type": "application/json", ...headers },
|
|
151
|
-
});
|
|
152
|
-
// Adapted from https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
|
|
153
|
-
if (response.status === 205) {
|
|
154
|
-
console.error("Stream already finished", response);
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
if (!response.ok) {
|
|
158
|
-
console.error("Failed to reach streaming endpoint", response);
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
if (!response.body) {
|
|
162
|
-
console.error("No body in response", response);
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
const reader = response.body.getReader();
|
|
166
|
-
while (true) {
|
|
167
|
-
try {
|
|
168
|
-
const { done, value } = await reader.read();
|
|
169
|
-
if (done) {
|
|
170
|
-
onUpdate(new TextDecoder().decode(value));
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
onUpdate(new TextDecoder().decode(value));
|
|
174
|
-
} catch (e) {
|
|
175
|
-
console.error("Error reading stream", e);
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|