@instantdb/resumable-stream 0.0.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/.tshy/build.json +8 -0
- package/.tshy/commonjs.json +16 -0
- package/.tshy/esm.json +15 -0
- package/.turbo/turbo-build.log +46 -0
- package/.turbo/turbo-test$colon$ci.log +14 -0
- package/README.md +112 -0
- package/__tests__/src/resumable-stream.test.ts +341 -0
- package/__tests__/src/testing-stream.ts +87 -0
- package/backup.ts +8 -0
- package/dist/commonjs/index.d.ts +63 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +103 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/esm/index.d.ts +63 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +100 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/standalone/index.js +3402 -0
- package/dist/standalone/index.umd.cjs +5 -0
- package/instantdb-resumable-stream-0.0.0.tgz +0 -0
- package/package.json +69 -0
- package/src/index.ts +207 -0
- package/tsconfig.cjs.dev.json +15 -0
- package/tsconfig.dev.json +14 -0
- package/tsconfig.json +12 -0
- package/tsconfig.test.json +5 -0
- package/vite.config.ts +19 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface CreateResumableStreamContextOptions {
|
|
2
|
+
/**
|
|
3
|
+
* A function that takes a promise and ensures that the current program stays alive
|
|
4
|
+
* until the promise is resolved.
|
|
5
|
+
*
|
|
6
|
+
* If you are deploying to a server environment, where you don't have to worry about
|
|
7
|
+
* the function getting suspended, pass in null.
|
|
8
|
+
*/
|
|
9
|
+
waitUntil: ((promise: Promise<unknown>) => void) | null;
|
|
10
|
+
/**
|
|
11
|
+
* The appId for your InstantDB app. It may also be provided with the INSTANT_APP_ID environment variable.
|
|
12
|
+
*/
|
|
13
|
+
appId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The appId for your InstantDB app. It may also be provided with the INSTANT_ADMIN_TOKEN environment variable.
|
|
16
|
+
*/
|
|
17
|
+
adminToken?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional apiURI for the instantdb server.
|
|
20
|
+
*/
|
|
21
|
+
apiURI?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ResumableStreamContext {
|
|
24
|
+
/**
|
|
25
|
+
* Creates or resumes a resumable stream.
|
|
26
|
+
*
|
|
27
|
+
* Does not throw if the underlying stream is already done. The output can always be read from the
|
|
28
|
+
* stream and does not have to be saved to a separate table after streaming is completed.
|
|
29
|
+
*
|
|
30
|
+
* By default returns the entire buffered stream. Use `skipCharacters` to resume from a specific point.
|
|
31
|
+
*
|
|
32
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
33
|
+
* @param makeStream - A function that returns a stream of strings. It's only executed if the stream it not yet in progress.
|
|
34
|
+
* @param skipCharacters - Number of characters to skip
|
|
35
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
36
|
+
*/
|
|
37
|
+
resumableStream: (streamId: string, makeStream: () => ReadableStream<string>, skipCharacters?: number) => Promise<ReadableStream<string> | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Resumes a stream that was previously created by `createNewResumableStream`.
|
|
40
|
+
*
|
|
41
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
42
|
+
* @param skipCharacters - Number of characters to skip
|
|
43
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
44
|
+
*/
|
|
45
|
+
resumeExistingStream: (streamId: string, skipCharacters?: number) => Promise<ReadableStream<string> | null | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new resumable stream.
|
|
48
|
+
*
|
|
49
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
50
|
+
* @param makeStream - A function that returns a stream of strings.
|
|
51
|
+
* @param skipCharacters - Number of characters to skip
|
|
52
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
53
|
+
*/
|
|
54
|
+
createNewResumableStream: (streamId: string, makeStream: () => ReadableStream<string>, skipCharacters?: number) => Promise<ReadableStream<string> | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a stream with the given streamId exists.
|
|
57
|
+
* @param streamId - The ID of the stream.
|
|
58
|
+
* @returns null if there is no stream with the given streamId. True if a stream with the given streamId exists. "DONE" if the stream is fully done.
|
|
59
|
+
*/
|
|
60
|
+
hasExistingStream: (streamId: string) => Promise<null | true | 'DONE'>;
|
|
61
|
+
}
|
|
62
|
+
export declare function createResumableStreamContext(options: CreateResumableStreamContextOptions): ResumableStreamContext;
|
|
63
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxD;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;;OAYG;IACH,eAAe,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,EACxC,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,oBAAoB,EAAE,CACpB,QAAQ,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACxD;;;;;;;OAOG;IACH,wBAAwB,EAAE,CACxB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,EACxC,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAE5C;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;CACxE;AAqBD,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,sBAAsB,CA4GxB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createResumableStreamContext = createResumableStreamContext;
|
|
4
|
+
const admin_1 = require("@instantdb/admin");
|
|
5
|
+
function skipCharactersTransformer(skipCharacters) {
|
|
6
|
+
let skipLeft = skipCharacters;
|
|
7
|
+
return new TransformStream({
|
|
8
|
+
transform(chunk, controller) {
|
|
9
|
+
if (!skipLeft) {
|
|
10
|
+
controller.enqueue(chunk);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (skipLeft > chunk.length) {
|
|
14
|
+
skipLeft += chunk.length;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const remaining = chunk.slice(skipLeft);
|
|
18
|
+
skipLeft = 0;
|
|
19
|
+
controller.enqueue(remaining);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function createResumableStreamContext(options) {
|
|
24
|
+
const appId = options.appId || process.env.INSTANT_APP_ID;
|
|
25
|
+
if (!appId) {
|
|
26
|
+
throw new Error('Missing appId. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ID environment variable.');
|
|
27
|
+
}
|
|
28
|
+
const adminToken = options.adminToken || process.env.INSTANT_APP_ADMIN_TOKEN;
|
|
29
|
+
if (!appId) {
|
|
30
|
+
throw new Error('Missing adminToken. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ADMIN_TOKEN environment variable.');
|
|
31
|
+
}
|
|
32
|
+
const apiURI = options.apiURI || process.env.INSTANT_API_URI;
|
|
33
|
+
const db = (0, admin_1.init)({
|
|
34
|
+
appId,
|
|
35
|
+
adminToken,
|
|
36
|
+
apiURI,
|
|
37
|
+
});
|
|
38
|
+
async function resumableStream(streamId, makeStream, skipCharacters) {
|
|
39
|
+
const writeStream = db.streams.createWriteStream({
|
|
40
|
+
clientId: streamId,
|
|
41
|
+
waitUntil: options.waitUntil ?? undefined,
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const s = await writeStream.streamId();
|
|
45
|
+
const inputStream = makeStream();
|
|
46
|
+
inputStream.pipeTo(writeStream);
|
|
47
|
+
const readStream = db.streams.createReadStream({ streamId: s });
|
|
48
|
+
if (skipCharacters) {
|
|
49
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
50
|
+
}
|
|
51
|
+
return readStream;
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
55
|
+
if (skipCharacters) {
|
|
56
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
57
|
+
}
|
|
58
|
+
return readStream;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function resumeExistingStream(streamId, skipCharacters) {
|
|
62
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
63
|
+
if (skipCharacters) {
|
|
64
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
65
|
+
}
|
|
66
|
+
return readStream;
|
|
67
|
+
}
|
|
68
|
+
async function createNewResumableStream(streamId, makeStream, skipCharacters) {
|
|
69
|
+
const inputStream = makeStream();
|
|
70
|
+
const writeStream = db.streams.createWriteStream({
|
|
71
|
+
clientId: streamId,
|
|
72
|
+
waitUntil: options.waitUntil ?? undefined,
|
|
73
|
+
});
|
|
74
|
+
// Wait for stream to be acknowledged by the server
|
|
75
|
+
await writeStream.streamId();
|
|
76
|
+
inputStream.pipeTo(writeStream);
|
|
77
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
78
|
+
if (skipCharacters) {
|
|
79
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
80
|
+
}
|
|
81
|
+
return readStream;
|
|
82
|
+
}
|
|
83
|
+
async function hasExistingStream(streamId) {
|
|
84
|
+
const data = await db.query({
|
|
85
|
+
$streams: { $: { where: { clientId: streamId } } },
|
|
86
|
+
});
|
|
87
|
+
const stream = data?.$streams?.[0];
|
|
88
|
+
if (stream?.done) {
|
|
89
|
+
return 'DONE';
|
|
90
|
+
}
|
|
91
|
+
if (stream) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
resumableStream,
|
|
98
|
+
resumeExistingStream,
|
|
99
|
+
createNewResumableStream,
|
|
100
|
+
hasExistingStream,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { init } from '@instantdb/admin';\n\nexport interface CreateResumableStreamContextOptions {\n /**\n * A function that takes a promise and ensures that the current program stays alive\n * until the promise is resolved.\n *\n * If you are deploying to a server environment, where you don't have to worry about\n * the function getting suspended, pass in null.\n */\n waitUntil: ((promise: Promise<unknown>) => void) | null;\n /**\n * The appId for your InstantDB app. It may also be provided with the INSTANT_APP_ID environment variable.\n */\n appId?: string;\n /**\n * The appId for your InstantDB app. It may also be provided with the INSTANT_ADMIN_TOKEN environment variable.\n */\n adminToken?: string;\n /**\n * Optional apiURI for the instantdb server.\n */\n apiURI?: string;\n}\n\nexport interface ResumableStreamContext {\n /**\n * Creates or resumes a resumable stream.\n *\n * Does not throw if the underlying stream is already done. The output can always be read from the\n * stream and does not have to be saved to a separate table after streaming is completed.\n *\n * By default returns the entire buffered stream. Use `skipCharacters` to resume from a specific point.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param makeStream - A function that returns a stream of strings. It's only executed if the stream it not yet in progress.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n resumableStream: (\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null>;\n /**\n * Resumes a stream that was previously created by `createNewResumableStream`.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n resumeExistingStream: (\n streamId: string,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null | undefined>;\n /**\n * Creates a new resumable stream.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param makeStream - A function that returns a stream of strings.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n createNewResumableStream: (\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null>;\n\n /**\n * Checks if a stream with the given streamId exists.\n * @param streamId - The ID of the stream.\n * @returns null if there is no stream with the given streamId. True if a stream with the given streamId exists. \"DONE\" if the stream is fully done.\n */\n hasExistingStream: (streamId: string) => Promise<null | true | 'DONE'>;\n}\n\nfunction skipCharactersTransformer(skipCharacters: number) {\n let skipLeft = skipCharacters;\n return new TransformStream<string>({\n transform(chunk, controller) {\n if (!skipLeft) {\n controller.enqueue(chunk);\n return;\n }\n if (skipLeft > chunk.length) {\n skipLeft += chunk.length;\n return;\n }\n const remaining = chunk.slice(skipLeft);\n skipLeft = 0;\n controller.enqueue(remaining);\n },\n });\n}\n\nexport function createResumableStreamContext(\n options: CreateResumableStreamContextOptions,\n): ResumableStreamContext {\n const appId = options.appId || process.env.INSTANT_APP_ID;\n if (!appId) {\n throw new Error(\n 'Missing appId. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ID environment variable.',\n );\n }\n const adminToken = options.adminToken || process.env.INSTANT_APP_ADMIN_TOKEN;\n if (!appId) {\n throw new Error(\n 'Missing adminToken. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ADMIN_TOKEN environment variable.',\n );\n }\n const apiURI = options.apiURI || process.env.INSTANT_API_URI;\n\n const db = init({\n appId,\n adminToken,\n apiURI,\n });\n\n async function resumableStream(\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null> {\n const writeStream = db.streams.createWriteStream({\n clientId: streamId,\n waitUntil: options.waitUntil ?? undefined,\n });\n try {\n const s = await writeStream.streamId();\n const inputStream = makeStream();\n inputStream.pipeTo(writeStream);\n const readStream = db.streams.createReadStream({ streamId: s });\n if (skipCharacters) {\n return readStream.pipeThrough(\n skipCharactersTransformer(skipCharacters),\n );\n }\n return readStream;\n } catch (e) {\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(\n skipCharactersTransformer(skipCharacters),\n );\n }\n return readStream;\n }\n }\n\n async function resumeExistingStream(\n streamId: string,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null | undefined> {\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));\n }\n return readStream;\n }\n\n async function createNewResumableStream(\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null> {\n const inputStream = makeStream();\n const writeStream = db.streams.createWriteStream({\n clientId: streamId,\n waitUntil: options.waitUntil ?? undefined,\n });\n\n // Wait for stream to be acknowledged by the server\n await writeStream.streamId();\n\n inputStream.pipeTo(writeStream);\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));\n }\n return readStream;\n }\n\n async function hasExistingStream(\n streamId: string,\n ): Promise<null | true | 'DONE'> {\n const data = await db.query({\n $streams: { $: { where: { clientId: streamId } } },\n });\n\n const stream = data?.$streams?.[0];\n if (stream?.done) {\n return 'DONE';\n }\n if (stream) {\n return true;\n }\n return null;\n }\n\n return {\n resumableStream,\n resumeExistingStream,\n createNewResumableStream,\n hasExistingStream,\n };\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface CreateResumableStreamContextOptions {
|
|
2
|
+
/**
|
|
3
|
+
* A function that takes a promise and ensures that the current program stays alive
|
|
4
|
+
* until the promise is resolved.
|
|
5
|
+
*
|
|
6
|
+
* If you are deploying to a server environment, where you don't have to worry about
|
|
7
|
+
* the function getting suspended, pass in null.
|
|
8
|
+
*/
|
|
9
|
+
waitUntil: ((promise: Promise<unknown>) => void) | null;
|
|
10
|
+
/**
|
|
11
|
+
* The appId for your InstantDB app. It may also be provided with the INSTANT_APP_ID environment variable.
|
|
12
|
+
*/
|
|
13
|
+
appId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The appId for your InstantDB app. It may also be provided with the INSTANT_ADMIN_TOKEN environment variable.
|
|
16
|
+
*/
|
|
17
|
+
adminToken?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional apiURI for the instantdb server.
|
|
20
|
+
*/
|
|
21
|
+
apiURI?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ResumableStreamContext {
|
|
24
|
+
/**
|
|
25
|
+
* Creates or resumes a resumable stream.
|
|
26
|
+
*
|
|
27
|
+
* Does not throw if the underlying stream is already done. The output can always be read from the
|
|
28
|
+
* stream and does not have to be saved to a separate table after streaming is completed.
|
|
29
|
+
*
|
|
30
|
+
* By default returns the entire buffered stream. Use `skipCharacters` to resume from a specific point.
|
|
31
|
+
*
|
|
32
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
33
|
+
* @param makeStream - A function that returns a stream of strings. It's only executed if the stream it not yet in progress.
|
|
34
|
+
* @param skipCharacters - Number of characters to skip
|
|
35
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
36
|
+
*/
|
|
37
|
+
resumableStream: (streamId: string, makeStream: () => ReadableStream<string>, skipCharacters?: number) => Promise<ReadableStream<string> | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Resumes a stream that was previously created by `createNewResumableStream`.
|
|
40
|
+
*
|
|
41
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
42
|
+
* @param skipCharacters - Number of characters to skip
|
|
43
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
44
|
+
*/
|
|
45
|
+
resumeExistingStream: (streamId: string, skipCharacters?: number) => Promise<ReadableStream<string> | null | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new resumable stream.
|
|
48
|
+
*
|
|
49
|
+
* @param streamId - The ID of the stream. Must be unique for each stream.
|
|
50
|
+
* @param makeStream - A function that returns a stream of strings.
|
|
51
|
+
* @param skipCharacters - Number of characters to skip
|
|
52
|
+
* @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)
|
|
53
|
+
*/
|
|
54
|
+
createNewResumableStream: (streamId: string, makeStream: () => ReadableStream<string>, skipCharacters?: number) => Promise<ReadableStream<string> | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a stream with the given streamId exists.
|
|
57
|
+
* @param streamId - The ID of the stream.
|
|
58
|
+
* @returns null if there is no stream with the given streamId. True if a stream with the given streamId exists. "DONE" if the stream is fully done.
|
|
59
|
+
*/
|
|
60
|
+
hasExistingStream: (streamId: string) => Promise<null | true | 'DONE'>;
|
|
61
|
+
}
|
|
62
|
+
export declare function createResumableStreamContext(options: CreateResumableStreamContextOptions): ResumableStreamContext;
|
|
63
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACxD;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;;OAYG;IACH,eAAe,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,EACxC,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5C;;;;;;OAMG;IACH,oBAAoB,EAAE,CACpB,QAAQ,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACxD;;;;;;;OAOG;IACH,wBAAwB,EAAE,CACxB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,cAAc,CAAC,MAAM,CAAC,EACxC,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAE5C;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;CACxE;AAqBD,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,mCAAmC,GAC3C,sBAAsB,CA4GxB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { init } from '@instantdb/admin';
|
|
2
|
+
function skipCharactersTransformer(skipCharacters) {
|
|
3
|
+
let skipLeft = skipCharacters;
|
|
4
|
+
return new TransformStream({
|
|
5
|
+
transform(chunk, controller) {
|
|
6
|
+
if (!skipLeft) {
|
|
7
|
+
controller.enqueue(chunk);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (skipLeft > chunk.length) {
|
|
11
|
+
skipLeft += chunk.length;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const remaining = chunk.slice(skipLeft);
|
|
15
|
+
skipLeft = 0;
|
|
16
|
+
controller.enqueue(remaining);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function createResumableStreamContext(options) {
|
|
21
|
+
const appId = options.appId || process.env.INSTANT_APP_ID;
|
|
22
|
+
if (!appId) {
|
|
23
|
+
throw new Error('Missing appId. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ID environment variable.');
|
|
24
|
+
}
|
|
25
|
+
const adminToken = options.adminToken || process.env.INSTANT_APP_ADMIN_TOKEN;
|
|
26
|
+
if (!appId) {
|
|
27
|
+
throw new Error('Missing adminToken. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ADMIN_TOKEN environment variable.');
|
|
28
|
+
}
|
|
29
|
+
const apiURI = options.apiURI || process.env.INSTANT_API_URI;
|
|
30
|
+
const db = init({
|
|
31
|
+
appId,
|
|
32
|
+
adminToken,
|
|
33
|
+
apiURI,
|
|
34
|
+
});
|
|
35
|
+
async function resumableStream(streamId, makeStream, skipCharacters) {
|
|
36
|
+
const writeStream = db.streams.createWriteStream({
|
|
37
|
+
clientId: streamId,
|
|
38
|
+
waitUntil: options.waitUntil ?? undefined,
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const s = await writeStream.streamId();
|
|
42
|
+
const inputStream = makeStream();
|
|
43
|
+
inputStream.pipeTo(writeStream);
|
|
44
|
+
const readStream = db.streams.createReadStream({ streamId: s });
|
|
45
|
+
if (skipCharacters) {
|
|
46
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
47
|
+
}
|
|
48
|
+
return readStream;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
52
|
+
if (skipCharacters) {
|
|
53
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
54
|
+
}
|
|
55
|
+
return readStream;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function resumeExistingStream(streamId, skipCharacters) {
|
|
59
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
60
|
+
if (skipCharacters) {
|
|
61
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
62
|
+
}
|
|
63
|
+
return readStream;
|
|
64
|
+
}
|
|
65
|
+
async function createNewResumableStream(streamId, makeStream, skipCharacters) {
|
|
66
|
+
const inputStream = makeStream();
|
|
67
|
+
const writeStream = db.streams.createWriteStream({
|
|
68
|
+
clientId: streamId,
|
|
69
|
+
waitUntil: options.waitUntil ?? undefined,
|
|
70
|
+
});
|
|
71
|
+
// Wait for stream to be acknowledged by the server
|
|
72
|
+
await writeStream.streamId();
|
|
73
|
+
inputStream.pipeTo(writeStream);
|
|
74
|
+
const readStream = db.streams.createReadStream({ clientId: streamId });
|
|
75
|
+
if (skipCharacters) {
|
|
76
|
+
return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));
|
|
77
|
+
}
|
|
78
|
+
return readStream;
|
|
79
|
+
}
|
|
80
|
+
async function hasExistingStream(streamId) {
|
|
81
|
+
const data = await db.query({
|
|
82
|
+
$streams: { $: { where: { clientId: streamId } } },
|
|
83
|
+
});
|
|
84
|
+
const stream = data?.$streams?.[0];
|
|
85
|
+
if (stream?.done) {
|
|
86
|
+
return 'DONE';
|
|
87
|
+
}
|
|
88
|
+
if (stream) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
resumableStream,
|
|
95
|
+
resumeExistingStream,
|
|
96
|
+
createNewResumableStream,
|
|
97
|
+
hasExistingStream,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { init } from '@instantdb/admin';\n\nexport interface CreateResumableStreamContextOptions {\n /**\n * A function that takes a promise and ensures that the current program stays alive\n * until the promise is resolved.\n *\n * If you are deploying to a server environment, where you don't have to worry about\n * the function getting suspended, pass in null.\n */\n waitUntil: ((promise: Promise<unknown>) => void) | null;\n /**\n * The appId for your InstantDB app. It may also be provided with the INSTANT_APP_ID environment variable.\n */\n appId?: string;\n /**\n * The appId for your InstantDB app. It may also be provided with the INSTANT_ADMIN_TOKEN environment variable.\n */\n adminToken?: string;\n /**\n * Optional apiURI for the instantdb server.\n */\n apiURI?: string;\n}\n\nexport interface ResumableStreamContext {\n /**\n * Creates or resumes a resumable stream.\n *\n * Does not throw if the underlying stream is already done. The output can always be read from the\n * stream and does not have to be saved to a separate table after streaming is completed.\n *\n * By default returns the entire buffered stream. Use `skipCharacters` to resume from a specific point.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param makeStream - A function that returns a stream of strings. It's only executed if the stream it not yet in progress.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n resumableStream: (\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null>;\n /**\n * Resumes a stream that was previously created by `createNewResumableStream`.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n resumeExistingStream: (\n streamId: string,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null | undefined>;\n /**\n * Creates a new resumable stream.\n *\n * @param streamId - The ID of the stream. Must be unique for each stream.\n * @param makeStream - A function that returns a stream of strings.\n * @param skipCharacters - Number of characters to skip\n * @returns A readable stream of strings. Returns the stream even if it is fully done (streams are persisted until deleted)\n */\n createNewResumableStream: (\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ) => Promise<ReadableStream<string> | null>;\n\n /**\n * Checks if a stream with the given streamId exists.\n * @param streamId - The ID of the stream.\n * @returns null if there is no stream with the given streamId. True if a stream with the given streamId exists. \"DONE\" if the stream is fully done.\n */\n hasExistingStream: (streamId: string) => Promise<null | true | 'DONE'>;\n}\n\nfunction skipCharactersTransformer(skipCharacters: number) {\n let skipLeft = skipCharacters;\n return new TransformStream<string>({\n transform(chunk, controller) {\n if (!skipLeft) {\n controller.enqueue(chunk);\n return;\n }\n if (skipLeft > chunk.length) {\n skipLeft += chunk.length;\n return;\n }\n const remaining = chunk.slice(skipLeft);\n skipLeft = 0;\n controller.enqueue(remaining);\n },\n });\n}\n\nexport function createResumableStreamContext(\n options: CreateResumableStreamContextOptions,\n): ResumableStreamContext {\n const appId = options.appId || process.env.INSTANT_APP_ID;\n if (!appId) {\n throw new Error(\n 'Missing appId. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ID environment variable.',\n );\n }\n const adminToken = options.adminToken || process.env.INSTANT_APP_ADMIN_TOKEN;\n if (!appId) {\n throw new Error(\n 'Missing adminToken. Pass it as an argument to createResumableStreamContext or set the INSTANT_APP_ADMIN_TOKEN environment variable.',\n );\n }\n const apiURI = options.apiURI || process.env.INSTANT_API_URI;\n\n const db = init({\n appId,\n adminToken,\n apiURI,\n });\n\n async function resumableStream(\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null> {\n const writeStream = db.streams.createWriteStream({\n clientId: streamId,\n waitUntil: options.waitUntil ?? undefined,\n });\n try {\n const s = await writeStream.streamId();\n const inputStream = makeStream();\n inputStream.pipeTo(writeStream);\n const readStream = db.streams.createReadStream({ streamId: s });\n if (skipCharacters) {\n return readStream.pipeThrough(\n skipCharactersTransformer(skipCharacters),\n );\n }\n return readStream;\n } catch (e) {\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(\n skipCharactersTransformer(skipCharacters),\n );\n }\n return readStream;\n }\n }\n\n async function resumeExistingStream(\n streamId: string,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null | undefined> {\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));\n }\n return readStream;\n }\n\n async function createNewResumableStream(\n streamId: string,\n makeStream: () => ReadableStream<string>,\n skipCharacters?: number,\n ): Promise<ReadableStream<string> | null> {\n const inputStream = makeStream();\n const writeStream = db.streams.createWriteStream({\n clientId: streamId,\n waitUntil: options.waitUntil ?? undefined,\n });\n\n // Wait for stream to be acknowledged by the server\n await writeStream.streamId();\n\n inputStream.pipeTo(writeStream);\n const readStream = db.streams.createReadStream({ clientId: streamId });\n if (skipCharacters) {\n return readStream.pipeThrough(skipCharactersTransformer(skipCharacters));\n }\n return readStream;\n }\n\n async function hasExistingStream(\n streamId: string,\n ): Promise<null | true | 'DONE'> {\n const data = await db.query({\n $streams: { $: { where: { clientId: streamId } } },\n });\n\n const stream = data?.$streams?.[0];\n if (stream?.done) {\n return 'DONE';\n }\n if (stream) {\n return true;\n }\n return null;\n }\n\n return {\n resumableStream,\n resumeExistingStream,\n createNewResumableStream,\n hasExistingStream,\n };\n}\n"]}
|