jbilcke-hf HF staff commited on
Commit
03a79fc
1 Parent(s): 4e412a8

fix issues

Browse files
packages/client/.gitignore ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2
+
3
+ # Logs
4
+
5
+ logs
6
+ _.log
7
+ npm-debug.log_
8
+ yarn-debug.log*
9
+ yarn-error.log*
10
+ lerna-debug.log*
11
+ .pnpm-debug.log*
12
+
13
+ # Diagnostic reports (https://nodejs.org/api/report.html)
14
+
15
+ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
16
+
17
+ # Runtime data
18
+
19
+ pids
20
+ _.pid
21
+ _.seed
22
+ \*.pid.lock
23
+
24
+ # Directory for instrumented libs generated by jscoverage/JSCover
25
+
26
+ lib-cov
27
+
28
+ # Coverage directory used by tools like istanbul
29
+
30
+ coverage
31
+ \*.lcov
32
+
33
+ # nyc test coverage
34
+
35
+ .nyc_output
36
+
37
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
38
+
39
+ .grunt
40
+
41
+ # Bower dependency directory (https://bower.io/)
42
+
43
+ bower_components
44
+
45
+ # node-waf configuration
46
+
47
+ .lock-wscript
48
+
49
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
50
+
51
+ build/Release
52
+
53
+ # Dependency directories
54
+
55
+ node_modules/
56
+ jspm_packages/
57
+
58
+ # Snowpack dependency directory (https://snowpack.dev/)
59
+
60
+ web_modules/
61
+
62
+ # TypeScript cache
63
+
64
+ \*.tsbuildinfo
65
+
66
+ # Optional npm cache directory
67
+
68
+ .npm
69
+
70
+ # Optional eslint cache
71
+
72
+ .eslintcache
73
+
74
+ # Optional stylelint cache
75
+
76
+ .stylelintcache
77
+
78
+ # Microbundle cache
79
+
80
+ .rpt2_cache/
81
+ .rts2_cache_cjs/
82
+ .rts2_cache_es/
83
+ .rts2_cache_umd/
84
+
85
+ # Optional REPL history
86
+
87
+ .node_repl_history
88
+
89
+ # Output of 'npm pack'
90
+
91
+ \*.tgz
92
+
93
+ # Yarn Integrity file
94
+
95
+ .yarn-integrity
96
+
97
+ # dotenv environment variable files
98
+
99
+ .env
100
+ .env.development.local
101
+ .env.test.local
102
+ .env.production.local
103
+ .env.local
104
+
105
+ # parcel-bundler cache (https://parceljs.org/)
106
+
107
+ .cache
108
+ .parcel-cache
109
+
110
+ # Next.js build output
111
+
112
+ .next
113
+ out
114
+
115
+ # Nuxt.js build / generate output
116
+ dist
117
+ .nuxt
118
+
119
+ # Gatsby files
120
+
121
+ .cache/
122
+
123
+ # Comment in the public line in if your project uses Gatsby and not Next.js
124
+
125
+ # https://nextjs.org/blog/next-9-1#public-directory-support
126
+
127
+ # public
128
+
129
+ # vuepress build output
130
+
131
+ .vuepress/dist
132
+
133
+ # vuepress v2.x temp and cache directory
134
+
135
+ .temp
136
+ .cache
137
+
138
+ # Docusaurus cache and generated files
139
+
140
+ .docusaurus
141
+
142
+ # Serverless directories
143
+
144
+ .serverless/
145
+
146
+ # FuseBox cache
147
+
148
+ .fusebox/
149
+
150
+ # DynamoDB Local files
151
+
152
+ .dynamodb/
153
+
154
+ # TernJS port file
155
+
156
+ .tern-port
157
+
158
+ # Stores VSCode versions used for testing VSCode extensions
159
+
160
+ .vscode-test
161
+
162
+ # yarn v2
163
+
164
+ .yarn/cache
165
+ .yarn/unplugged
166
+ .yarn/build-state.yml
167
+ .yarn/install-state.gz
168
+ .pnp.\*
169
+
170
+ # IntelliJ based IDEs
171
+ .idea
172
+
173
+ # Finder (MacOS) folder config
174
+ .DS_Store
175
+
176
+ # TypeScript build information
177
+ *.tsbuildinfo
packages/client/.npmignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Ignore everything
2
+ *
3
+ # Except the dist directory
4
+ !dist/
packages/client/.prettierrc.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "arrowParens": "avoid",
5
+ "printWidth": 140,
6
+ "tabWidth": 2,
7
+ "trailingComma": "es5",
8
+ "bracketSpacing": true
9
+ }
packages/client/LICENSE.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ Copyright (c) 2024 Julian Bilcke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
packages/client/README.md ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @aitube/client
2
+
3
+ *Official API client for AiTube.at*
4
+
5
+ ## ATTENTION
6
+
7
+ AiTube is currently in heavy development, and for the moment
8
+ the API client is reserved for *private use* (it is used by AI Stories Factory).
9
+
10
+ We are sorry for any inconvenience this might cause.
11
+
12
+ ## Caveats
13
+
14
+ The official domain for AiTube is `aitube.at`, however right now
15
+ the Hugging Face Space is not configured to use this as a domain,
16
+ so we need to perform all API calls to `jbilcke-hf-ai-tube.hf.space`.
17
+
18
+ ## Installation
19
+
20
+ To install the package, run the following command:
21
+
22
+ ```bash
23
+ npm install @aitube/client
24
+ ```
25
+
26
+ ## Getting Started
27
+
28
+ Note: to overridethe AiTube API URL, set this env var: `AITUBE_URL`
29
+
30
+ ```typescript
31
+ import {
32
+ createClap,
33
+ editClapDialogues,
34
+ editClapEntities,
35
+ editClapMusic,
36
+ editClapSounds,
37
+ editClapStory,
38
+ editClapStoryboards,
39
+ editClapVideos,
40
+ exportClapToVideo,
41
+ defaultAitubeHostname,
42
+ defaultClapWidth,
43
+ defaultClapHeight,
44
+ defaultExportFormat,
45
+ aitubeUrl,
46
+ aitubeApiVersion,
47
+ aitubeApiUrl,
48
+ ClapEntityPrompt,
49
+ SupportedExportFormat,
50
+ applyClapCompletion,
51
+ } from '@aitube/client'
52
+
53
+ const ultraSecret = "ultra secret token unavailable to common mortals"
54
+
55
+ const basicClap = await createClap({
56
+ prompt: "story about a dog",
57
+ turbo: false,
58
+ token: ultraSecret
59
+ })
60
+
61
+ const illustratedClap = await editClapStoryboards({
62
+ clap: basicClap,
63
+ turbo: false,
64
+ token: ultraSecret
65
+ })
66
+
67
+ const mp4VideoFile = await exportClapToVideo({
68
+ clap: illustratedClap,
69
+ format: "mp4",
70
+ turbo: false,
71
+ token: ultraSecret
72
+ })
73
+ ```
74
+
75
+ ## Customizing the server
76
+
77
+ The hostname can be overriden by defining the `AITUBE_URL` environment variable.
78
+
79
+ eg:
80
+
81
+ ```bash
82
+ AITUBE_URL=http://localhost:3000
83
+ ```
84
+
85
+ ## Build Instructions
86
+
87
+ Install [Bun](https://bun.sh/)
88
+
89
+ Run the following commands:
90
+
91
+ ```bash
92
+ bun install
93
+
94
+ bun run build
95
+ ```
96
+
97
+ To publish:
98
+
99
+ ```bash
100
+ bun run build
101
+
102
+ bun run build:declaration
103
+
104
+ bun run publish
105
+ ```
106
+
107
+ ## Contributing
108
+
109
+ We welcome contributions! Please feel free to submit a pull request.
110
+
111
+ ## License
112
+
113
+ This package is under the MIT License. See `LICENSE` file for more details.
packages/client/package.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@aitube/client",
3
+ "module": "index.ts",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "type": "module",
7
+ "version": "0.2.4-3",
8
+ "description": "Official API client for AiTube.at",
9
+ "scripts": {
10
+ "build": "bun build ./src/index.ts --outfile=dist/index.js --external=@aitube/clap && bun run build:declaration",
11
+ "build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json",
12
+ "postbuild": "rimraf tsconfig.types.tsbuildinfo && bun run build:declaration",
13
+ "publish": "npm publish --access public",
14
+ "update": "rm -Rf node_modules && rm bun.lockb && bun i && bun run build"
15
+ },
16
+ "devDependencies": {
17
+ "bun-types": "latest",
18
+ "prettier": "^3.3.3",
19
+ "rimraf": "^6.0.1",
20
+ "typescript": "^5.5.4"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/jbilcke-hf/aitube-client.git"
25
+ },
26
+ "keywords": [
27
+ "Clapper.app",
28
+ "AiTube.at",
29
+ "OpenClap",
30
+ "AI cinema",
31
+ "file format",
32
+ "specification"
33
+ ],
34
+ "author": "Julian Bilcke",
35
+ "license": "MIT",
36
+ "files": [
37
+ "dist/*.js",
38
+ "dist/*.d.ts",
39
+ "dist/**/*.d.ts"
40
+ ],
41
+ "dependencies": {
42
+ "@aitube/clap": "workspace:*",
43
+ "@types/bun": "latest",
44
+ "query-string": "^9.0.0"
45
+ }
46
+ }
packages/client/src/api/createClap.ts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapProject, fetchClap } from "@aitube/clap"
2
+
3
+ import { aitubeApiUrl } from "@/constants/config"
4
+
5
+ import { defaultClapHeight, defaultClapWidth } from "@/constants/defaultValues"
6
+
7
+ export async function createClap({
8
+ prompt,
9
+ height = defaultClapHeight,
10
+ width = defaultClapWidth,
11
+ turbo = false,
12
+ token,
13
+ }: {
14
+ prompt: string
15
+ height?: number
16
+ width?: number
17
+ turbo?: boolean
18
+ token?: string
19
+ }): Promise<ClapProject> {
20
+
21
+ if (typeof prompt !== "string" || !prompt.length) { throw new Error(`please provide a prompt`) }
22
+
23
+ const hasToken = typeof token === "string" && token.length > 0
24
+
25
+ const clap = await fetchClap(`${aitubeApiUrl}create`, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ ...hasToken && {
30
+ "Authorization": `Bearer ${token}`
31
+ }
32
+ },
33
+ body: JSON.stringify({
34
+ prompt,
35
+ width,
36
+ height,
37
+ turbo,
38
+ }),
39
+ cache: "no-store",
40
+ })
41
+
42
+ return clap
43
+ }
packages/client/src/api/editClapDialogues.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapCompletionMode, ClapProject, fetchClap, removeGeneratedAssetUrls, serializeClap } from "@aitube/clap"
2
+ import queryString from "query-string"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapDialogues({
8
+ clap,
9
+ completionMode = ClapCompletionMode.MERGE,
10
+ turbo = false,
11
+ token,
12
+ }: {
13
+ clap: ClapProject
14
+
15
+ /**
16
+ * Completion mode (optional, defaults to "merge")
17
+ *
18
+ * Possible values are:
19
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
20
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
21
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
22
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
23
+ */
24
+ completionMode?: ClapCompletionMode
25
+
26
+ turbo?: boolean
27
+
28
+ token?: string
29
+ }): Promise<ClapProject> {
30
+
31
+ if (!clap) { throw new Error(`please provide a valid clap project`) }
32
+
33
+ const hasToken = typeof token === "string" && token.length > 0
34
+
35
+ const params: Record<string, any> = {}
36
+
37
+ if (typeof completionMode === "string") {
38
+ params.c = completionMode
39
+ }
40
+
41
+ if (turbo) {
42
+ params.t = "true"
43
+ }
44
+
45
+ const newClap = await fetchClap(
46
+ `${aitubeApiUrl}edit/dialogues?${queryString.stringify(params)}`, {
47
+ method: "POST",
48
+ headers: {
49
+ "Content-Type": "application/x-gzip",
50
+ ...hasToken && {
51
+ "Authorization": `Bearer ${token}`
52
+ }
53
+ },
54
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
55
+ cache: "no-store",
56
+ })
57
+
58
+ const result = await applyClapCompletion(clap, newClap, completionMode)
59
+
60
+ return result
61
+ }
packages/client/src/api/editClapEntities.ts ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapCompletionMode, ClapProject, fetchClap, serializeClap, removeGeneratedAssetUrls } from "@aitube/clap"
2
+ import queryString from "query-string"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { ClapEntityPrompt } from "@/constants/types"
6
+ import { applyClapCompletion } from "@/utils"
7
+
8
+ export async function editClapEntities({
9
+ clap,
10
+ entityPrompts = [],
11
+ completionMode = ClapCompletionMode.MERGE,
12
+ turbo = false,
13
+ token,
14
+ }: {
15
+ // A ClapProject instance
16
+ clap: ClapProject
17
+
18
+ // a list of entity prompts
19
+ entityPrompts?: ClapEntityPrompt[],
20
+
21
+ /**
22
+ * Completion mode (optional, defaults to "merge")
23
+ *
24
+ * Possible values are:
25
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
26
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
27
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
28
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
29
+ */
30
+ completionMode?: ClapCompletionMode
31
+
32
+ turbo?: boolean
33
+
34
+ token?: string
35
+ }): Promise<ClapProject> {
36
+
37
+ if (!clap) { throw new Error(`please provide a clap to extend`) }
38
+
39
+ const hasToken = typeof token === "string" && token.length > 0
40
+
41
+ const params: Record<string, any> = {}
42
+
43
+ if (typeof completionMode === "string") {
44
+ params.c = completionMode
45
+ }
46
+
47
+ if (turbo) {
48
+ params.t = "true"
49
+ }
50
+
51
+ if (entityPrompts.length) {
52
+ // if "params.e = JSON.stringify(item)" works with UTF-8 characters,
53
+ // then we don't need to import "js-base64"
54
+ // otherwise you will have to do:
55
+ // params.e = jsBase64.encode(JSON.stringify(item))
56
+ params.e = JSON.stringify(entityPrompts)
57
+ }
58
+
59
+ const newClap = await fetchClap(
60
+ `${aitubeApiUrl}edit/entities?${queryString.stringify(params)}`, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/x-gzip",
64
+ ...hasToken && {
65
+ "Authorization": `Bearer ${token}`
66
+ }
67
+ },
68
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
69
+ cache: "no-store",
70
+ })
71
+
72
+ const result = await applyClapCompletion(clap, newClap, completionMode)
73
+
74
+ return result
75
+ }
packages/client/src/api/editClapMusic.ts ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import queryString from "query-string"
2
+ import { ClapCompletionMode, ClapProject, fetchClap, serializeClap, removeGeneratedAssetUrls } from "@aitube/clap"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapMusic({
8
+ clap,
9
+ completionMode = ClapCompletionMode.MERGE,
10
+ turbo = false,
11
+ token,
12
+ }: {
13
+ clap: ClapProject
14
+
15
+ /**
16
+ * Completion mode (optional, defaults to "merge")
17
+ *
18
+ * Possible values are:
19
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
20
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
21
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
22
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
23
+ */
24
+ completionMode?: ClapCompletionMode
25
+
26
+ turbo?: boolean
27
+
28
+ token?: string
29
+ }): Promise<ClapProject> {
30
+
31
+ if (!clap) { throw new Error(`please provide a valid clap project`) }
32
+
33
+ const hasToken = typeof token === "string" && token.length > 0
34
+
35
+ const params: Record<string, any> = {}
36
+
37
+ if (typeof completionMode === "string") {
38
+ params.c = completionMode
39
+ }
40
+
41
+ if (turbo) {
42
+ params.t = "true"
43
+ }
44
+
45
+ const newClap = await fetchClap(`${aitubeApiUrl}edit/music?${queryString.stringify(params)}`, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/x-gzip",
49
+ ...hasToken && {
50
+ "Authorization": `Bearer ${token}`
51
+ }
52
+ },
53
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
54
+ cache: "no-store",
55
+ })
56
+
57
+ const result = await applyClapCompletion(clap, newClap, completionMode)
58
+
59
+ return result
60
+ }
packages/client/src/api/editClapSounds.ts ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import queryString from "query-string"
2
+ import { ClapCompletionMode, ClapProject, fetchClap, serializeClap, removeGeneratedAssetUrls } from "@aitube/clap"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapSounds({
8
+ clap,
9
+ completionMode = ClapCompletionMode.MERGE,
10
+ turbo = false,
11
+ token,
12
+ }: {
13
+ clap: ClapProject
14
+
15
+ /**
16
+ * Completion mode (optional, defaults to "merge")
17
+ *
18
+ * Possible values are:
19
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
20
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
21
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
22
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
23
+ */
24
+ completionMode?: ClapCompletionMode
25
+
26
+ turbo?: boolean
27
+
28
+ token?: string
29
+ }): Promise<ClapProject> {
30
+
31
+ if (!clap) { throw new Error(`please provide a valid clap project`) }
32
+
33
+ const hasToken = typeof token === "string" && token.length > 0
34
+
35
+ const params: Record<string, any> = {}
36
+
37
+ if (typeof completionMode === "string") {
38
+ params.c = completionMode
39
+ }
40
+
41
+ if (turbo) {
42
+ params.t = "true"
43
+ }
44
+
45
+ const newClap = await fetchClap(`${aitubeApiUrl}edit/sounds?${queryString.stringify(params)}`, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/x-gzip",
49
+ ...hasToken && {
50
+ "Authorization": `Bearer ${token}`
51
+ }
52
+ },
53
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
54
+ cache: "no-store",
55
+ })
56
+
57
+ const result = await applyClapCompletion(clap, newClap, completionMode)
58
+
59
+ return result
60
+ }
packages/client/src/api/editClapStory.ts ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapCompletionMode, ClapProject, fetchClap, filterAssets, isValidNumber, serializeClap, removeGeneratedAssetUrls } from "@aitube/clap"
2
+ import queryString from "query-string"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapStory({
8
+ clap,
9
+ prompt,
10
+ startTimeInMs,
11
+ endTimeInMs,
12
+ completionMode = ClapCompletionMode.MERGE,
13
+ turbo = false,
14
+ token,
15
+ }: {
16
+ // A ClapProject instance
17
+ clap: ClapProject
18
+
19
+ // a prompt to describe how to extend the story (optional)
20
+ prompt?: string
21
+
22
+ // indicates where the completion should start in the timeline
23
+ //
24
+ // this can be used tp jump to arbitrary timestamps
25
+ // in the story
26
+ //
27
+ // if you pick a start time AFTER the current project's end time,
28
+ // the project will be extended
29
+ //
30
+ // default value: the current project's end time
31
+ startTimeInMs?: number
32
+
33
+ // it is recommended to use a
34
+ // end time (eg. startTimeInMs + 12000)
35
+ // if left by default, th server will generate
36
+ //
37
+ // for performance and security reasons,
38
+ // the server may enforce a hardcoded limit to bypass what you
39
+ // set here, but that is not a big deal because you can pass
40
+ // you .clap file again if necessary
41
+ //
42
+ // default value: no limit (the server will set one)
43
+ endTimeInMs?: number
44
+
45
+ /**
46
+ * Completion mode (optional, defaults to "merge")
47
+ *
48
+ * Possible values are:
49
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
50
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
51
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
52
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
53
+ */
54
+ completionMode?: ClapCompletionMode
55
+
56
+ turbo?: boolean
57
+
58
+ token?: string
59
+ }): Promise<ClapProject> {
60
+
61
+ if (!clap) { throw new Error(`please provide a clap to extend`) }
62
+
63
+ const hasToken = typeof token === "string" && token.length > 0
64
+
65
+ const params: Record<string, any> = {}
66
+
67
+ if (typeof completionMode === "string") {
68
+ params.c = completionMode
69
+ }
70
+
71
+ if (typeof prompt === "string" && prompt.length > 0) {
72
+ params.p = prompt
73
+ }
74
+
75
+ if (isValidNumber(startTimeInMs)) {
76
+ params.s = startTimeInMs
77
+ }
78
+
79
+ if (isValidNumber(endTimeInMs)) {
80
+ params.e = endTimeInMs
81
+ }
82
+
83
+ if (turbo) {
84
+ params.t = "true"
85
+ }
86
+
87
+ // we remove heavy elements from the payload
88
+ const payload = await filterAssets({
89
+ clap,
90
+ mode: "INCLUDE",
91
+ categories: {},
92
+ immutable: true, // to create a standalone copy
93
+
94
+ // we only remove the data, but we still keep things marked as "generated"
95
+ updateStatus: false,
96
+ })
97
+
98
+ const newClap = await fetchClap(
99
+ `${aitubeApiUrl}edit/story?${queryString.stringify(params)}`, {
100
+ method: "POST",
101
+ headers: {
102
+ "Content-Type": "application/x-gzip",
103
+ ...hasToken && {
104
+ "Authorization": `Bearer ${token}`
105
+ }
106
+ },
107
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
108
+ cache: "no-store",
109
+ })
110
+
111
+ const result = await applyClapCompletion(clap, newClap, completionMode)
112
+
113
+ return result
114
+ }
packages/client/src/api/editClapStoryboards.ts ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import queryString from "query-string"
2
+ import { ClapCompletionMode, ClapProject, fetchClap, serializeClap, removeGeneratedAssetUrls } from "@aitube/clap"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapStoryboards({
8
+ clap,
9
+ completionMode = ClapCompletionMode.MERGE,
10
+ turbo = false,
11
+ token,
12
+ }: {
13
+ clap: ClapProject
14
+
15
+ /**
16
+ * Completion mode (optional, defaults to "merge")
17
+ *
18
+ * Possible values are:
19
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
20
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
21
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
22
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
23
+ */
24
+ completionMode?: ClapCompletionMode
25
+
26
+ turbo?: boolean
27
+
28
+ token?: string
29
+ }): Promise<ClapProject> {
30
+
31
+ if (!clap) { throw new Error(`please provide a valid clap project`) }
32
+
33
+ const hasToken = typeof token === "string" && token.length > 0
34
+
35
+ const params: Record<string, any> = {}
36
+
37
+ if (typeof completionMode === "string") {
38
+ params.c = completionMode
39
+ }
40
+
41
+ if (turbo) {
42
+ params.t = "true"
43
+ }
44
+
45
+ const newClap = await fetchClap(`${aitubeApiUrl}edit/storyboards?${queryString.stringify(params)}`, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/x-gzip",
49
+ ...hasToken && {
50
+ "Authorization": `Bearer ${token}`
51
+ }
52
+ },
53
+ body: await serializeClap(removeGeneratedAssetUrls(clap)),
54
+ cache: "no-store",
55
+ })
56
+
57
+ const result = await applyClapCompletion(clap, newClap, completionMode)
58
+
59
+ return result
60
+ }
packages/client/src/api/editClapVideos.ts ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import queryString from "query-string"
2
+ import { ClapCompletionMode, ClapProject, fetchClap, serializeClap, removeGeneratedAssetUrls, ClapSegmentStatus, ClapSegmentCategory, filterSegments, ClapSegmentFilteringMode, ClapSegment } from "@aitube/clap"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { applyClapCompletion } from "@/utils"
6
+
7
+ export async function editClapVideos({
8
+ clap,
9
+ completionMode = ClapCompletionMode.MERGE,
10
+ turbo = false,
11
+ token,
12
+ }: {
13
+ clap: ClapProject
14
+
15
+ /**
16
+ * Completion mode (optional, defaults to "merge")
17
+ *
18
+ * Possible values are:
19
+ * - full: the API and the client will return a full clap file. This is a very convenient and simple mode, but it is also very ineficient, so it should not be used for intensive applications.
20
+ * - partial: the API and the client will return a partial clap file, containing only the new values and changes. This is useful for real-time applications and streaming.
21
+ * - merge: the API will return a partial clap file, and the client will return a merge of the original with the new values. This is safe to run, there are no side-effects.
22
+ * - replace: the API will return a partial clap file, and the client will replace the original. This is the most efficient mode, but it relies on side-effects and inline object updates.
23
+ */
24
+ completionMode?: ClapCompletionMode
25
+
26
+ turbo?: boolean
27
+
28
+ token?: string
29
+ }): Promise<ClapProject> {
30
+
31
+ if (!clap) { throw new Error(`please provide a valid clap project`) }
32
+
33
+ const hasToken = typeof token === "string" && token.length > 0
34
+
35
+ const params: Record<string, any> = {}
36
+
37
+ if (typeof completionMode === "string") {
38
+ params.c = completionMode
39
+ }
40
+
41
+ if (turbo) {
42
+ params.t = "true"
43
+ }
44
+ // special trick to not touch the generated
45
+ // storyboard images that are used by pending videos
46
+ const idsOfStoryboardsToKeep = clap.segments.map((segment: ClapSegment) => {
47
+
48
+ const isPendingVideo = (
49
+ segment.category === ClapSegmentCategory.VIDEO
50
+ &&
51
+ segment.status === ClapSegmentStatus.TO_GENERATE
52
+ )
53
+
54
+ if (!isPendingVideo) { return undefined }
55
+
56
+ const storyboardImage: ClapSegment | undefined = filterSegments(
57
+ ClapSegmentFilteringMode.BOTH,
58
+ segment,
59
+ clap.segments,
60
+ ClapSegmentCategory.IMAGE
61
+ ).at(0)
62
+
63
+ return storyboardImage?.id
64
+ }).filter((x: any) => x) as string[]
65
+
66
+ const newClap = await fetchClap(`${aitubeApiUrl}edit/videos?${queryString.stringify(params)}`, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/x-gzip",
70
+ ...hasToken && {
71
+ "Authorization": `Bearer ${token}`
72
+ }
73
+ },
74
+ body: await serializeClap(
75
+ // need a special trick here, to not touch the generated
76
+ // storyboards that are used by pending videos
77
+ removeGeneratedAssetUrls(clap, idsOfStoryboardsToKeep)
78
+ ),
79
+ cache: "no-store",
80
+ })
81
+
82
+ const result = await applyClapCompletion(clap, newClap, completionMode)
83
+
84
+ return result
85
+ }
packages/client/src/api/exportClapToVideo.ts ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import queryString from "query-string"
2
+ import { ClapProject, serializeClap, blobToDataUri } from "@aitube/clap"
3
+
4
+ import { aitubeApiUrl } from "@/constants/config"
5
+ import { defaultExportFormat, SupportedExportFormat } from "@/constants"
6
+
7
+
8
+ export async function exportClapToVideo({
9
+ clap,
10
+ format = defaultExportFormat,
11
+ turbo = false,
12
+ token,
13
+ }: {
14
+ clap: ClapProject
15
+
16
+ /**
17
+ * Desired output video format (defaults to "mp4")
18
+ *
19
+ * Can be either "mp4" or "webm"
20
+ */
21
+ format?: SupportedExportFormat
22
+
23
+ turbo?: boolean
24
+
25
+ token?: string
26
+ }): Promise<string> {
27
+
28
+ if (!clap) { throw new Error(`please provide a clap`) }
29
+
30
+ // TODO use an enum instead, and check the enum object
31
+ if (format !== "mp4" && format !== "webm") { throw new Error(`please provide a valid format ("${format}" is unrecognized)`) }
32
+
33
+ const params: Record<string, any> = {}
34
+
35
+ params.f = format
36
+
37
+ if (turbo) {
38
+ params.t = "true"
39
+ }
40
+
41
+ const hasToken = typeof token === "string" && token.length > 0
42
+
43
+ const res = await fetch(`${aitubeApiUrl}export?${queryString.stringify(params)}`, {
44
+ method: "POST",
45
+ headers: {
46
+ "Content-Type": "application/x-gzip",
47
+ ...hasToken && {
48
+ "Authorization": `Bearer ${token}`
49
+ }
50
+ },
51
+ body: await serializeClap(clap),
52
+ cache: "no-store",
53
+ })
54
+
55
+ const blob = await res.blob()
56
+
57
+ const dataURL = await blobToDataUri(blob)
58
+
59
+ return dataURL
60
+ }
packages/client/src/api/index.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ export { createClap } from "./createClap"
2
+ export { editClapDialogues } from "./editClapDialogues"
3
+ export { editClapEntities } from "./editClapEntities"
4
+ export { editClapMusic } from "./editClapMusic"
5
+ export { editClapSounds } from "./editClapSounds"
6
+ export { editClapStory } from "./editClapStory"
7
+ export { editClapStoryboards } from "./editClapStoryboards"
8
+ export { editClapVideos } from "./editClapVideos"
9
+ export { exportClapToVideo } from "./exportClapToVideo"
packages/client/src/constants/config.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defaultAitubeHostname } from "./defaultValues"
2
+
3
+ // we leave the opportunity to override this at runtime
4
+ export const aitubeUrl = `${
5
+ process.env.AITUBE_URL ||
6
+ `https://${defaultAitubeHostname}`
7
+ }`
8
+
9
+ // note: let's keep it simple and only support one version at a time
10
+ export const aitubeApiVersion = "v1"
11
+
12
+ export const aitubeApiUrl = `${
13
+ aitubeUrl
14
+ }${
15
+ aitubeUrl.endsWith("/") ? "" : "/"
16
+ }api/${aitubeApiVersion}/`
packages/client/src/constants/defaultValues.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // unfortunately, this doesn't work yet due to a redirection issue
2
+ // const defaultAitubeHostname = "aitube.at"
3
+
4
+ // so we have to use the direct space hostname instead
5
+ export const defaultAitubeHostname = "aitube.at"
6
+
7
+ export const defaultClapWidth = 512
8
+ export const defaultClapHeight = 288
9
+
10
+ export const defaultExportFormat = "mp4"
packages/client/src/constants/index.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export {
2
+ defaultAitubeHostname,
3
+ defaultClapWidth,
4
+ defaultClapHeight,
5
+ defaultExportFormat
6
+ } from './defaultValues'
7
+
8
+ export {
9
+ aitubeUrl,
10
+ aitubeApiVersion,
11
+ aitubeApiUrl
12
+ } from './config'
13
+
14
+ export {
15
+ ClapEntityPrompt,
16
+ SupportedExportFormat
17
+ } from "./types"
packages/client/src/constants/types.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapSegmentCategory } from "@aitube/clap"
2
+
3
+ export type SupportedExportFormat = "mp4" | "webm"
4
+
5
+ export type ClapEntityPrompt = {
6
+ name: string
7
+
8
+ // eg. "character", "location"
9
+ category: ClapSegmentCategory
10
+
11
+ // age of the person, animal or entity (eg. robot, talking spaceship etc)
12
+ age: string
13
+
14
+ // characterization of the person, animal or entity (texture, hair color, gender etc)
15
+ variant: string
16
+
17
+ // region from where the person, animal or entity is coming from (human, mechanical, alien planet, european, south-american etc)
18
+ region: string
19
+
20
+ // identity picture
21
+ identityImage: string
22
+
23
+ // identity voice
24
+ identityVoice: string
25
+ }
packages/client/src/index.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ export {
3
+ createClap,
4
+ editClapDialogues,
5
+ editClapEntities,
6
+ editClapMusic,
7
+ editClapSounds,
8
+ editClapStory,
9
+ editClapStoryboards,
10
+ editClapVideos,
11
+ exportClapToVideo,
12
+ } from './api'
13
+
14
+ export {
15
+ defaultAitubeHostname,
16
+ defaultClapWidth,
17
+ defaultClapHeight,
18
+ defaultExportFormat,
19
+ aitubeUrl,
20
+ aitubeApiVersion,
21
+ aitubeApiUrl,
22
+ ClapEntityPrompt,
23
+ SupportedExportFormat
24
+ } from "./constants"
25
+
26
+ export {
27
+ applyClapCompletion,
28
+ } from "./utils"
packages/client/src/parsers/index.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ export { parseEntityPrompt } from "./parseEntityPrompt"
2
+ export { parseString } from "./parseString"
3
+ export { parseStringArray } from "./parseStringArray"
packages/client/src/parsers/parseEntityPrompt.ts ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ import { ClapEntityPrompt } from "@/constants/types"
4
+
5
+ import { parseString } from "./parseString"
6
+ import { ClapSegmentCategory } from "@aitube/clap"
7
+
8
+ export function parseEntityPrompt(entityPrompt: Partial<ClapEntityPrompt> = {}): ClapEntityPrompt {
9
+
10
+ return {
11
+ name: parseString(entityPrompt?.name),
12
+
13
+ category: parseString(entityPrompt?.category) as ClapSegmentCategory,
14
+
15
+ // age of the person, animal or entity (eg. robot, talking spaceship etc)
16
+ age: parseString(entityPrompt?.age),
17
+
18
+ // characterization of the person, animal or entity (texture, hair color, gender etc)
19
+ variant: parseString(entityPrompt?.variant),
20
+
21
+ // region from where the person, animal or entity is coming from (human, mechanical, alien planet, european, south-american etc)
22
+ region: parseString(entityPrompt?.region),
23
+
24
+ // identity picture
25
+ identityImage: parseString(entityPrompt?.identityImage),
26
+
27
+ // identity voice
28
+ identityVoice: parseString(entityPrompt?.identityVoice),
29
+ }
30
+ }
packages/client/src/parsers/parseString.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ export function parseString(input?: any, defaultValue?: string): string {
2
+ const defValue = `${defaultValue || ""}`
3
+
4
+ if (typeof input !== "string") { return defValue }
5
+
6
+ return input || defValue
7
+ }
packages/client/src/parsers/parseStringArray.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ export function parseStringArray(something: any): string[] {
2
+ let result: string[] = []
3
+ if (typeof something === "string") {
4
+ result = [something]
5
+ } else if (Array.isArray(something)) {
6
+ result = something.map(thing => typeof thing === "string" ? thing : "").filter(x => x)
7
+ }
8
+ return result
9
+ }
packages/client/src/utils/applyClapCompletion.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ClapCompletionMode, ClapProject, updateClap } from "@aitube/clap"
2
+
3
+ export async function applyClapCompletion(
4
+ existingClap: ClapProject,
5
+ newerClap: ClapProject,
6
+ clapCompletionMode: ClapCompletionMode
7
+ ): Promise<ClapProject> {
8
+ // in both those mode we leave full control to what is inside "newerClap"
9
+ if (clapCompletionMode === ClapCompletionMode.FULL || clapCompletionMode === ClapCompletionMode.PARTIAL) {
10
+ return newerClap
11
+ }
12
+
13
+ // else we are in ClapCompletionMode.MERGE or ClapCompletionMode.REPLACE
14
+ const result = await updateClap(existingClap, newerClap, {
15
+ // the newer clap meta may contain incomplete information
16
+ overwriteMeta: false,
17
+ inlineReplace: clapCompletionMode === ClapCompletionMode.REPLACE
18
+ })
19
+
20
+ return result
21
+ }
packages/client/src/utils/index.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ export { applyClapCompletion } from "./applyClapCompletion"
packages/client/tsconfig.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "./dist",
4
+ "rootDir": "./src",
5
+ "baseUrl": "./",
6
+ "paths": {
7
+ "@/*": ["src/*"]
8
+ },
9
+ "lib": ["ESNext", "DOM"],
10
+ "module": "esnext",
11
+ "target": "esnext",
12
+ "moduleResolution": "bundler",
13
+ "moduleDetection": "force",
14
+ "allowImportingTsExtensions": true,
15
+ "noEmit": true,
16
+ "composite": true,
17
+ "strict": true,
18
+ "downlevelIteration": true,
19
+ "skipLibCheck": true,
20
+ "jsx": "react-jsx",
21
+ "allowSyntheticDefaultImports": true,
22
+ "forceConsistentCasingInFileNames": true,
23
+ "allowJs": true,
24
+ "types": [
25
+ "bun-types"
26
+ ]
27
+ },
28
+ "include": [
29
+ "src/**/*.ts"
30
+ ]
31
+ }
packages/client/tsconfig.types.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "emitDeclarationOnly": true,
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ },
10
+ "include": [
11
+ "src/**/*.ts"
12
+ ]
13
+ }