@tscircuit/cli 0.0.5 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -22
- package/bun.lockb +0 -0
- package/dev-server-frontend/bun.lockb +0 -0
- package/dist/cli.js +557 -211
- package/lib/cmd-fns/add.ts +34 -0
- package/lib/cmd-fns/config-reveal-location.ts +1 -1
- package/lib/cmd-fns/dev/check-if-initialized.ts +22 -0
- package/lib/cmd-fns/dev/index.ts +25 -9
- package/lib/cmd-fns/dev/infer-export-name-from-source.ts +17 -0
- package/lib/cmd-fns/dev/soupify-and-upload-example-file.ts +2 -17
- package/lib/cmd-fns/dev-server-upload.ts +26 -0
- package/lib/cmd-fns/index.ts +5 -0
- package/lib/cmd-fns/init/create-or-modify-npmrc.ts +21 -0
- package/lib/cmd-fns/init/get-generated-npmrc.ts +8 -0
- package/lib/cmd-fns/init/get-generated-readme.ts +34 -0
- package/lib/cmd-fns/init/get-generated-tsconfig.ts +34 -0
- package/lib/cmd-fns/{init.ts → init/index.ts} +26 -63
- package/lib/cmd-fns/install.ts +34 -0
- package/lib/cmd-fns/publish/index.ts +296 -0
- package/lib/cmd-fns/remove.ts +31 -0
- package/lib/cmd-fns/uninstall.ts +31 -0
- package/lib/get-program.ts +48 -7
- package/lib/util/create-context-and-run-program.ts +1 -1
- package/lib/util/get-all-package-files.ts +35 -0
- package/package.json +9 -5
- package/tests/assets/example-project/README.md +18 -0
- package/tests/assets/example-project/index.ts +1 -0
- package/tests/assets/example-project/package.json +2 -1
- package/tests/init.test.ts +1 -1
- package/lib/cmd-fns/publish.ts +0 -3
package/dist/cli.js
CHANGED
|
@@ -210,12 +210,13 @@ import {Command} from "commander";
|
|
|
210
210
|
// package.json
|
|
211
211
|
var package_default = {
|
|
212
212
|
name: "@tscircuit/cli",
|
|
213
|
-
version: "0.0.
|
|
213
|
+
version: "0.0.8",
|
|
214
214
|
private: false,
|
|
215
215
|
type: "module",
|
|
216
216
|
description: "Command line tool for developing, publishing and installing tscircuit circuits",
|
|
217
217
|
main: "./dist/cli.js",
|
|
218
218
|
scripts: {
|
|
219
|
+
bootstrap: "bun i && cd dev-server-api && bun i && cd ../dev-server-frontend && bun i",
|
|
219
220
|
start: "bun cli.ts",
|
|
220
221
|
"start:dev-server:dev": "TSCI_DEV_SERVER_DB=$(pwd)/.tscircuit/dev-server.db concurrently 'cd dev-server-api && bun start' 'cd dev-server-frontend && bun start'",
|
|
221
222
|
"start:dev-server": "bun build:dev-server && bun cli.ts dev -y --cwd ./tests/assets/example-project",
|
|
@@ -226,8 +227,7 @@ var package_default = {
|
|
|
226
227
|
},
|
|
227
228
|
bin: {
|
|
228
229
|
tscircuit: "./dist/cli.js",
|
|
229
|
-
tsci: "./dist/cli.js"
|
|
230
|
-
tsck: "./dist/cli.js"
|
|
230
|
+
tsci: "./dist/cli.js"
|
|
231
231
|
},
|
|
232
232
|
keywords: [],
|
|
233
233
|
author: "",
|
|
@@ -245,7 +245,9 @@ var package_default = {
|
|
|
245
245
|
"dax-sh": "^0.39.2",
|
|
246
246
|
delay: "^6.0.0",
|
|
247
247
|
edgespec: "^0.0.69",
|
|
248
|
+
glob: "^10.3.10",
|
|
248
249
|
hono: "^4.1.0",
|
|
250
|
+
ignore: "^5.3.1",
|
|
249
251
|
kleur: "^4.1.5",
|
|
250
252
|
"kysely-bun-sqlite": "^0.3.2",
|
|
251
253
|
lodash: "^4.17.21",
|
|
@@ -253,9 +255,10 @@ var package_default = {
|
|
|
253
255
|
minimist: "^1.2.8",
|
|
254
256
|
"node-persist": "^4.0.1",
|
|
255
257
|
open: "^10.1.0",
|
|
256
|
-
"perfect-cli": "1.0.
|
|
258
|
+
"perfect-cli": "^1.0.16",
|
|
257
259
|
prompts: "^2.4.2",
|
|
258
260
|
react: "^18.2.0",
|
|
261
|
+
semver: "^7.6.0",
|
|
259
262
|
zod: "latest"
|
|
260
263
|
},
|
|
261
264
|
devDependencies: {
|
|
@@ -268,6 +271,7 @@ var package_default = {
|
|
|
268
271
|
"@types/node": "^20.10.6",
|
|
269
272
|
"@types/prompts": "^2.4.9",
|
|
270
273
|
"@types/react": "^18.2.64",
|
|
274
|
+
"@types/semver": "^7.5.8",
|
|
271
275
|
ava: "^6.1.1",
|
|
272
276
|
concurrently: "^8.2.2",
|
|
273
277
|
tsx: "^4.7.1",
|
|
@@ -277,7 +281,7 @@ var package_default = {
|
|
|
277
281
|
|
|
278
282
|
// lib/cmd-fns/config-reveal-location.ts
|
|
279
283
|
var configRevealLocation = async (ctx, args) => {
|
|
280
|
-
console.log(`
|
|
284
|
+
console.log(`tsci config path: ${ctx.global_config.path}`);
|
|
281
285
|
};
|
|
282
286
|
// lib/cmd-fns/config-set-registry.ts
|
|
283
287
|
import {z} from "zod";
|
|
@@ -601,17 +605,245 @@ var packageExamplesCreate = async (ctx, args) => {
|
|
|
601
605
|
}).then((r) => r.data.package_example);
|
|
602
606
|
console.log(package_example);
|
|
603
607
|
};
|
|
604
|
-
// lib/cmd-fns/publish.ts
|
|
608
|
+
// lib/cmd-fns/publish/index.ts
|
|
609
|
+
import kleur3 from "kleur";
|
|
610
|
+
import {z as z14} from "zod";
|
|
611
|
+
import * as Path2 from "path";
|
|
612
|
+
import * as fs5 from "fs/promises";
|
|
613
|
+
import {readFileSync} from "fs";
|
|
614
|
+
|
|
615
|
+
// lib/util/get-all-package-files.ts
|
|
616
|
+
import * as fs4 from "fs/promises";
|
|
617
|
+
import * as Glob from "glob";
|
|
618
|
+
import ignore from "ignore";
|
|
619
|
+
var getAllPackageFiles = async (ctx) => {
|
|
620
|
+
const gitignore = await fs4.readFile("./.gitignore").then((b) => b.toString().split("\n").filter(Boolean)).catch((e) => null);
|
|
621
|
+
const npmignore = await fs4.readFile("./.promptignore").then((b) => b.toString().split("\n").filter(Boolean)).catch((e) => null);
|
|
622
|
+
const ig = ignore().add([
|
|
623
|
+
...npmignore ?? gitignore ?? [],
|
|
624
|
+
".tscircuit",
|
|
625
|
+
"node_modules/*"
|
|
626
|
+
]);
|
|
627
|
+
return Glob.globSync("**/*.{ts,tsx,md}", {}).filter((fp) => !ig.ignores(fp));
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// lib/cmd-fns/publish/index.ts
|
|
631
|
+
import prompts from "prompts";
|
|
632
|
+
|
|
633
|
+
// lib/cmd-fns/init/get-generated-readme.ts
|
|
634
|
+
var getGeneratedReadme = ({
|
|
635
|
+
name,
|
|
636
|
+
shouldHaveProjectGeneratedNotice = false
|
|
637
|
+
}) => {
|
|
638
|
+
return `
|
|
639
|
+
# ${name}
|
|
640
|
+
${shouldHaveProjectGeneratedNotice ? `\n\n> This project was generated using [tsci](https://github.com/tscircuit/tscircuit)\n` : ""}
|
|
641
|
+
To develop and view the examples, run \`tsci dev\` and open [http://localhost:3020](http://localhost:3020) in your browser.
|
|
642
|
+
|
|
643
|
+
## Developing
|
|
644
|
+
|
|
645
|
+
You should install an editor like [VS Code](https://code.visualstudio.com/) with a typescript extension. Many web developers already have this.
|
|
646
|
+
|
|
647
|
+
Usually, you'll want to develop some named circuits inside of the \`lib\` directory,
|
|
648
|
+
export them in the \`index.ts\` file, then show how to use them in the \`examples\` directory.
|
|
649
|
+
|
|
650
|
+
Any file in \`examples\` will be automatically loaded and appear in the browser preview when you run \`tsci dev\`. It auto-reloads when you make changes, no need to reload the page or re-run the command.
|
|
651
|
+
|
|
652
|
+
> [!TIP] Make sure to replace this README with some details about your project and how to use it.
|
|
653
|
+
|
|
654
|
+
## Publishing
|
|
655
|
+
|
|
656
|
+
After you're satisfied with your project, publish it on the [tscircuit registry](https://registry.tscircuit.com) with \`tsci publish\`
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
`.trim();
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
// lib/cmd-fns/dev/infer-export-name-from-source.ts
|
|
663
|
+
var inferExportNameFromSource = (sourceContent) => {
|
|
664
|
+
if (sourceContent.includes("export default")) {
|
|
665
|
+
return "default";
|
|
666
|
+
}
|
|
667
|
+
const matches = Array.from(sourceContent.matchAll(/export\s+(const|function)\s+([A-Z]\w+)\s*=?/g)).map((m) => m[2]);
|
|
668
|
+
if (matches.length === 0) {
|
|
669
|
+
throw new Error(`No export detected in "${sourceContent}"`);
|
|
670
|
+
}
|
|
671
|
+
if (matches.length > 1) {
|
|
672
|
+
throw new Error(`Multiple exports detected in "${sourceContent}", only single exports currently working`);
|
|
673
|
+
}
|
|
674
|
+
return matches[0];
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
// lib/cmd-fns/publish/index.ts
|
|
678
|
+
import $2 from "dax-sh";
|
|
679
|
+
import semver from "semver";
|
|
680
|
+
import {unlink as unlink2} from "fs/promises";
|
|
605
681
|
var publish = async (ctx, args) => {
|
|
682
|
+
const params = z14.object({
|
|
683
|
+
increment: z14.boolean().optional(),
|
|
684
|
+
patch: z14.boolean().optional(),
|
|
685
|
+
lock: z14.boolean().optional()
|
|
686
|
+
}).parse(args);
|
|
687
|
+
if (typeof Bun === "undefined") {
|
|
688
|
+
console.log(kleur3.red("\n\n----------------------------------\nBun is currently required for publishing packages to the tscircuit registry. Try installing bun:\nhhttps://bun.sh/docs/installation\n\n---------------------"));
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
const shouldIncrement = params.increment || params.patch;
|
|
692
|
+
if (!await fs5.exists(Path2.join(ctx.cwd, "package.json"))) {
|
|
693
|
+
console.log(kleur3.red("No package.json found in current directory"));
|
|
694
|
+
process.exit(1);
|
|
695
|
+
}
|
|
696
|
+
const packageJson = JSON.parse(await readFileSync(Path2.join(ctx.cwd, "package.json"), "utf-8"));
|
|
697
|
+
await Bun.build({
|
|
698
|
+
root: ctx.cwd,
|
|
699
|
+
entrypoints: ["index.ts"],
|
|
700
|
+
external: [
|
|
701
|
+
...Object.keys(packageJson.dependencies || {}),
|
|
702
|
+
...Object.keys(packageJson.devDependencies || {}),
|
|
703
|
+
...Object.keys(packageJson.peerDependencies || {}),
|
|
704
|
+
...Object.keys(packageJson.trustedDependencies || {})
|
|
705
|
+
],
|
|
706
|
+
outdir: "dist",
|
|
707
|
+
target: "node"
|
|
708
|
+
});
|
|
709
|
+
let { name, version } = packageJson;
|
|
710
|
+
name = name.replace(/^@/, "");
|
|
711
|
+
const existingPackage = await ctx.axios.post("/packages/get", { name }).then((r) => r.data.package).catch((e) => {
|
|
712
|
+
if (e.response?.data?.error?.error_code === "package_not_found")
|
|
713
|
+
return null;
|
|
714
|
+
throw e;
|
|
715
|
+
});
|
|
716
|
+
if (!existingPackage) {
|
|
717
|
+
if (!packageJson.name.includes("/")) {
|
|
718
|
+
console.log(kleur3.yellow(`Package name "${packageJson.name}" is not scoped. Scoped package names are recommended on the tscircuit registry.`));
|
|
719
|
+
const myAccount = await ctx.axios.get("/accounts/get").then((r) => r.data.account);
|
|
720
|
+
const newScopedName = `${myAccount.github_username}/${packageJson.name}`;
|
|
721
|
+
const { confirmNameChange } = await prompts({
|
|
722
|
+
type: "confirm",
|
|
723
|
+
name: "confirmNameChange",
|
|
724
|
+
initial: true,
|
|
725
|
+
message: `Would you like to change the package name to the scoped name "@${newScopedName}"?`
|
|
726
|
+
});
|
|
727
|
+
if (confirm === undefined) {
|
|
728
|
+
console.log(kleur3.red("Aborted."));
|
|
729
|
+
process.exit(1);
|
|
730
|
+
}
|
|
731
|
+
if (confirmNameChange) {
|
|
732
|
+
packageJson.name = `@${newScopedName}`;
|
|
733
|
+
await fs5.writeFile(Path2.join(ctx.cwd, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
734
|
+
name = newScopedName;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
console.log(kleur3.green(`Creating package "${packageJson.name}" on tscircuit registry...`));
|
|
738
|
+
let description = packageJson.description;
|
|
739
|
+
if (!description) {
|
|
740
|
+
description = (await prompts({
|
|
741
|
+
type: "text",
|
|
742
|
+
name: "description",
|
|
743
|
+
message: "Enter a description for the package"
|
|
744
|
+
})).description;
|
|
745
|
+
}
|
|
746
|
+
await ctx.axios.post("/packages/create", { name, description }).then((r) => r.data.package);
|
|
747
|
+
}
|
|
748
|
+
const existingRelease = await ctx.axios.post("/package_releases/get", {
|
|
749
|
+
package_name_with_version: `${name}@${version}`
|
|
750
|
+
}).then((r) => r.data.package_release).catch((e) => {
|
|
751
|
+
if (e.response?.data?.error?.error_code === "package_release_not_found")
|
|
752
|
+
return null;
|
|
753
|
+
throw e;
|
|
754
|
+
});
|
|
755
|
+
if (existingRelease) {
|
|
756
|
+
console.log(kleur3.gray(`Package release already exists: ${name}@${version}`));
|
|
757
|
+
if (shouldIncrement) {
|
|
758
|
+
console.log(kleur3.green(`Incrementing version from ${version} to ${semver.inc(version, "patch")}...`));
|
|
759
|
+
version = semver.inc(version, "patch");
|
|
760
|
+
packageJson.version = version;
|
|
761
|
+
await fs5.writeFile(Path2.join(ctx.cwd, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
762
|
+
} else {
|
|
763
|
+
console.log(kleur3.blue(`Want to increment the version and publish a new release? Use "--increment"!`));
|
|
764
|
+
throw new Error("Package release already exists");
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const newRelease = await ctx.axios.post("/package_releases/create", {
|
|
768
|
+
package_name_with_version: `${name}@${version}`,
|
|
769
|
+
is_latest: false
|
|
770
|
+
}).then((r) => r.data.package_release);
|
|
771
|
+
const filePaths = await getAllPackageFiles(ctx);
|
|
772
|
+
if (!filePaths.includes("README.md")) {
|
|
773
|
+
console.log(kleur3.yellow("No README.md found in package files. A README.md is recommended on the tscircuit registry."));
|
|
774
|
+
const { confirmReadme } = await prompts({
|
|
775
|
+
type: "confirm",
|
|
776
|
+
name: "confirmReadme",
|
|
777
|
+
initial: true,
|
|
778
|
+
message: "Would you like to add a README.md?"
|
|
779
|
+
});
|
|
780
|
+
if (confirmReadme === undefined) {
|
|
781
|
+
console.log(kleur3.red("Aborted."));
|
|
782
|
+
process.exit(1);
|
|
783
|
+
}
|
|
784
|
+
if (confirmReadme) {
|
|
785
|
+
await fs5.writeFile(Path2.join(ctx.cwd, "README.md"), getGeneratedReadme({ name }));
|
|
786
|
+
filePaths.push("README.md");
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
for (const filePath of filePaths) {
|
|
790
|
+
const fileContent = await fs5.readFile(Path2.join(ctx.cwd, filePath));
|
|
791
|
+
await ctx.axios.post("/package_files/create", {
|
|
792
|
+
file_path: filePath,
|
|
793
|
+
content_text: fileContent.toString(),
|
|
794
|
+
package_name_with_version: `${name}@${version}`
|
|
795
|
+
}).then((r) => r.data.package_file);
|
|
796
|
+
}
|
|
797
|
+
const exampleFilePaths = filePaths.filter((fp) => fp.startsWith("examples/"));
|
|
798
|
+
for (const filePath of exampleFilePaths) {
|
|
799
|
+
const fileContent = (await fs5.readFile(Path2.join(ctx.cwd, filePath))).toString();
|
|
800
|
+
const exportName = inferExportNameFromSource(fileContent);
|
|
801
|
+
const tscircuit_soup = await soupify({
|
|
802
|
+
filePath,
|
|
803
|
+
exportName
|
|
804
|
+
}).catch((e) => e);
|
|
805
|
+
if (tscircuit_soup instanceof Error) {
|
|
806
|
+
console.log(kleur3.red(`Error soupifying ${filePath}: ${tscircuit_soup}, skipping`));
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
await ctx.axios.post("/package_examples/create", {
|
|
810
|
+
file_path: filePath,
|
|
811
|
+
package_name_with_version: `${name}@${version}`,
|
|
812
|
+
export_name: exportName,
|
|
813
|
+
source_content: fileContent,
|
|
814
|
+
tscircuit_soup
|
|
815
|
+
}).then((r) => r.data.package_example);
|
|
816
|
+
}
|
|
817
|
+
const tmpTarballPath = Path2.join(ctx.cwd, ".tscircuit/tmp", `${name.replace(/\//g, "-")}-${version}.tgz`);
|
|
818
|
+
await fs5.mkdir(Path2.dirname(tmpTarballPath), { recursive: true });
|
|
819
|
+
const npm_pack_outputs = await $2`cd ${ctx.cwd} && npm pack --json --pack-destination ${Path2.dirname(tmpTarballPath)}`.json();
|
|
820
|
+
if (!await fs5.exists(tmpTarballPath)) {
|
|
821
|
+
console.log(kleur3.red(`Couldn't find tarball at ${tmpTarballPath}`));
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
await ctx.axios.post("/package_files/create", {
|
|
825
|
+
file_path: `.tscircuit-internal/tarball.tgz`,
|
|
826
|
+
content_base64: (await fs5.readFile(tmpTarballPath)).toString("base64"),
|
|
827
|
+
package_name_with_version: `${name}@${version}`,
|
|
828
|
+
is_release_tarball: true,
|
|
829
|
+
npm_pack_output: npm_pack_outputs?.[0]
|
|
830
|
+
});
|
|
831
|
+
await unlink2(tmpTarballPath);
|
|
832
|
+
await ctx.axios.post("/package_releases/update", {
|
|
833
|
+
package_name_with_version: `${name}@${version}`,
|
|
834
|
+
is_locked: params.lock ? true : false,
|
|
835
|
+
is_latest: true
|
|
836
|
+
});
|
|
837
|
+
console.log(kleur3.green(`Published ${name}@${version}!\nhttps://registry.tscircuit.com/${name}`));
|
|
606
838
|
};
|
|
607
839
|
// lib/cmd-fns/soupify.ts
|
|
608
|
-
import {z as
|
|
840
|
+
import {z as z15} from "zod";
|
|
609
841
|
import {writeFileSync as writeFileSync2} from "fs";
|
|
610
842
|
var soupifyCmd = async (ctx, args) => {
|
|
611
|
-
const params =
|
|
612
|
-
file:
|
|
613
|
-
export:
|
|
614
|
-
output:
|
|
843
|
+
const params = z15.object({
|
|
844
|
+
file: z15.string(),
|
|
845
|
+
export: z15.string().optional(),
|
|
846
|
+
output: z15.string().optional()
|
|
615
847
|
}).parse(args);
|
|
616
848
|
const soup = await soupify({
|
|
617
849
|
filePath: params.file,
|
|
@@ -624,9 +856,9 @@ var soupifyCmd = async (ctx, args) => {
|
|
|
624
856
|
}
|
|
625
857
|
};
|
|
626
858
|
// lib/cmd-fns/dev/index.ts
|
|
627
|
-
import {z as
|
|
628
|
-
import
|
|
629
|
-
import
|
|
859
|
+
import {z as z18} from "zod";
|
|
860
|
+
import kleur10 from "kleur";
|
|
861
|
+
import prompts2 from "prompts";
|
|
630
862
|
import open from "open";
|
|
631
863
|
|
|
632
864
|
// dev-server-api/dist/bundle.ts
|
|
@@ -635,10 +867,10 @@ import {makeRequestAgainstEdgeSpec} from "edgespec";
|
|
|
635
867
|
import {createWithEdgeSpec} from "edgespec";
|
|
636
868
|
import {Kysely, sql, SqliteDialect} from "kysely";
|
|
637
869
|
import {mkdirSync} from "fs";
|
|
638
|
-
import * as
|
|
639
|
-
import
|
|
870
|
+
import * as Path3 from "path";
|
|
871
|
+
import kleur4 from "kleur";
|
|
640
872
|
import"edgespec/middleware";
|
|
641
|
-
import {z as
|
|
873
|
+
import {z as z16} from "zod";
|
|
642
874
|
import {NotFoundError as NotFoundError2} from "edgespec/middleware";
|
|
643
875
|
import {z as z22} from "zod";
|
|
644
876
|
import {z as z32} from "zod";
|
|
@@ -653,7 +885,7 @@ var getDb = async () => {
|
|
|
653
885
|
if (globalDb)
|
|
654
886
|
return globalDb;
|
|
655
887
|
const devServerDbPath = process.env.TSCI_DEV_SERVER_DB ?? "./.tscircuit/dev-server.db";
|
|
656
|
-
mkdirSync(
|
|
888
|
+
mkdirSync(Path3.dirname(devServerDbPath), { recursive: true });
|
|
657
889
|
let dialect;
|
|
658
890
|
if (typeof Bun !== "undefined") {
|
|
659
891
|
try {
|
|
@@ -702,7 +934,7 @@ var withErrorResponse = async (req, ctx, next) => {
|
|
|
702
934
|
try {
|
|
703
935
|
return await next(req, ctx);
|
|
704
936
|
} catch (error) {
|
|
705
|
-
console.error(
|
|
937
|
+
console.error(kleur4.red("Intercepted error:"), error);
|
|
706
938
|
if (error instanceof Response) {
|
|
707
939
|
return error;
|
|
708
940
|
}
|
|
@@ -721,17 +953,17 @@ var withEdgeSpec = createWithEdgeSpec({
|
|
|
721
953
|
});
|
|
722
954
|
var create_default = withEdgeSpec({
|
|
723
955
|
methods: ["POST"],
|
|
724
|
-
jsonBody:
|
|
725
|
-
file_path:
|
|
726
|
-
export_name:
|
|
727
|
-
tscircuit_soup:
|
|
956
|
+
jsonBody: z16.object({
|
|
957
|
+
file_path: z16.string(),
|
|
958
|
+
export_name: z16.string().default("default"),
|
|
959
|
+
tscircuit_soup: z16.any()
|
|
728
960
|
}),
|
|
729
|
-
jsonResponse:
|
|
730
|
-
dev_package_example:
|
|
731
|
-
dev_package_example_id:
|
|
732
|
-
file_path:
|
|
733
|
-
tscircuit_soup:
|
|
734
|
-
last_updated_at:
|
|
961
|
+
jsonResponse: z16.object({
|
|
962
|
+
dev_package_example: z16.object({
|
|
963
|
+
dev_package_example_id: z16.coerce.number(),
|
|
964
|
+
file_path: z16.string(),
|
|
965
|
+
tscircuit_soup: z16.any(),
|
|
966
|
+
last_updated_at: z16.string().datetime()
|
|
735
967
|
})
|
|
736
968
|
}),
|
|
737
969
|
auth: "none"
|
|
@@ -914,15 +1146,15 @@ var startDevServer = async ({
|
|
|
914
1146
|
};
|
|
915
1147
|
|
|
916
1148
|
// lib/cmd-fns/dev/get-dev-server-axios.ts
|
|
917
|
-
import
|
|
1149
|
+
import kleur5 from "kleur";
|
|
918
1150
|
import defaultAxios from "axios";
|
|
919
1151
|
var getDevServerAxios = ({ serverUrl }) => {
|
|
920
1152
|
const devServerAxios = defaultAxios.create({
|
|
921
1153
|
baseURL: serverUrl
|
|
922
1154
|
});
|
|
923
1155
|
devServerAxios.interceptors.response.use((res) => res, (err) => {
|
|
924
|
-
console.log(
|
|
925
|
-
console.log(
|
|
1156
|
+
console.log(kleur5.red(`[ERR] ${err.response?.status} ${err.config.method?.toUpperCase()} ${err.config.url}\n\n${JSON.stringify(err.response?.data, null, " ")}`.replace(/\\n/g, "\n").replace(/\\"/g, '"')));
|
|
1157
|
+
console.log(kleur5.yellow("[Request Body]:"), err.config.data);
|
|
926
1158
|
return Promise.reject(err);
|
|
927
1159
|
});
|
|
928
1160
|
return devServerAxios;
|
|
@@ -933,9 +1165,9 @@ import {join as joinPath2} from "path";
|
|
|
933
1165
|
import {readdirSync as readdirSync2} from "fs";
|
|
934
1166
|
|
|
935
1167
|
// lib/cmd-fns/dev/soupify-and-upload-example-file.ts
|
|
936
|
-
import
|
|
1168
|
+
import kleur6 from "kleur";
|
|
937
1169
|
import {join as joinPath} from "path";
|
|
938
|
-
import {readFileSync} from "fs";
|
|
1170
|
+
import {readFileSync as readFileSync2} from "fs";
|
|
939
1171
|
var soupifyAndUploadExampleFile = async ({
|
|
940
1172
|
examplesDir,
|
|
941
1173
|
exampleFileName,
|
|
@@ -943,32 +1175,22 @@ var soupifyAndUploadExampleFile = async ({
|
|
|
943
1175
|
}) => {
|
|
944
1176
|
try {
|
|
945
1177
|
const examplePath = joinPath(examplesDir, exampleFileName);
|
|
946
|
-
const exampleContent =
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const matches = Array.from(exampleContent.matchAll(/export\s+(const|function)\s+([A-Z]\w+)\s*=?/g)).map((m) => m[2]);
|
|
950
|
-
if (matches.length === 0) {
|
|
951
|
-
throw new Error(`No export detected in "${exampleFileName}"`);
|
|
952
|
-
}
|
|
953
|
-
if (matches.length > 1) {
|
|
954
|
-
throw new Error(`Multiple exports detected in "${exampleFileName}", only single exports currently working`);
|
|
955
|
-
}
|
|
956
|
-
exportName = matches[0];
|
|
957
|
-
}
|
|
958
|
-
console.log(kleur5.gray(`[soupifying] ${exampleFileName}...`));
|
|
1178
|
+
const exampleContent = readFileSync2(examplePath).toString();
|
|
1179
|
+
const exportName = inferExportNameFromSource(exampleContent);
|
|
1180
|
+
console.log(kleur6.gray(`[soupifying] ${exampleFileName}...`));
|
|
959
1181
|
const soup = await soupify({
|
|
960
1182
|
filePath: examplePath,
|
|
961
1183
|
exportName
|
|
962
1184
|
});
|
|
963
|
-
console.log(
|
|
1185
|
+
console.log(kleur6.gray(`[uploading ] ${exampleFileName}...`));
|
|
964
1186
|
await devServerAxios.post("/api/dev_package_examples/create", {
|
|
965
1187
|
tscircuit_soup: soup,
|
|
966
1188
|
file_path: examplePath,
|
|
967
1189
|
export_name: exportName
|
|
968
1190
|
});
|
|
969
|
-
console.log(
|
|
1191
|
+
console.log(kleur6.gray(`[ done ] ${exampleFileName}!`));
|
|
970
1192
|
} catch (e) {
|
|
971
|
-
console.log(
|
|
1193
|
+
console.log(kleur6.red(e.toString()));
|
|
972
1194
|
}
|
|
973
1195
|
};
|
|
974
1196
|
|
|
@@ -993,12 +1215,12 @@ var uploadExamplesFromDirectory = async ({
|
|
|
993
1215
|
};
|
|
994
1216
|
|
|
995
1217
|
// lib/cmd-fns/dev/index.ts
|
|
996
|
-
import {unlink as
|
|
997
|
-
import * as
|
|
1218
|
+
import {unlink as unlink3} from "fs/promises";
|
|
1219
|
+
import * as Path7 from "path";
|
|
998
1220
|
|
|
999
1221
|
// lib/cmd-fns/dev/start-watcher.ts
|
|
1000
1222
|
import chokidar from "chokidar";
|
|
1001
|
-
import
|
|
1223
|
+
import kleur7 from "kleur";
|
|
1002
1224
|
var startWatcher = async ({
|
|
1003
1225
|
cwd,
|
|
1004
1226
|
devServerAxios
|
|
@@ -1018,7 +1240,7 @@ var startWatcher = async ({
|
|
|
1018
1240
|
async function uploadInBackground() {
|
|
1019
1241
|
while (upload_queue_state.should_run) {
|
|
1020
1242
|
if (upload_queue_state.dirty) {
|
|
1021
|
-
console.log(
|
|
1243
|
+
console.log(kleur7.yellow("Changes detected, re-uploading examples..."));
|
|
1022
1244
|
upload_queue_state.dirty = false;
|
|
1023
1245
|
await uploadExamplesFromDirectory({ cwd, devServerAxios });
|
|
1024
1246
|
}
|
|
@@ -1035,61 +1257,96 @@ var startWatcher = async ({
|
|
|
1035
1257
|
};
|
|
1036
1258
|
};
|
|
1037
1259
|
|
|
1038
|
-
// lib/cmd-fns/
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
type: "select",
|
|
1057
|
-
name: "action",
|
|
1058
|
-
message: "Action:",
|
|
1059
|
-
choices: [
|
|
1060
|
-
{
|
|
1061
|
-
title: "Open in Browser",
|
|
1062
|
-
value: "open-in-browser"
|
|
1063
|
-
},
|
|
1064
|
-
{
|
|
1065
|
-
title: "Stop Server",
|
|
1066
|
-
value: "stop"
|
|
1067
|
-
}
|
|
1068
|
-
]
|
|
1069
|
-
});
|
|
1070
|
-
if (action === "open-in-browser") {
|
|
1071
|
-
open(serverUrl);
|
|
1072
|
-
} else if (!action || action === "stop") {
|
|
1073
|
-
if (server.stop)
|
|
1074
|
-
server.stop();
|
|
1075
|
-
if (server.close)
|
|
1076
|
-
server.close();
|
|
1077
|
-
watcher.stop();
|
|
1078
|
-
break;
|
|
1260
|
+
// lib/cmd-fns/init/create-or-modify-npmrc.ts
|
|
1261
|
+
import {existsSync, writeFileSync as writeFileSync3} from "fs";
|
|
1262
|
+
import kleur8 from "kleur";
|
|
1263
|
+
import * as Path4 from "path";
|
|
1264
|
+
|
|
1265
|
+
// lib/cmd-fns/init/get-generated-npmrc.ts
|
|
1266
|
+
var getGeneratedNpmrc = (ctx) => `
|
|
1267
|
+
|
|
1268
|
+
@tsci:registry=${ctx.registry_url}/npm
|
|
1269
|
+
|
|
1270
|
+
`.trim();
|
|
1271
|
+
|
|
1272
|
+
// lib/cmd-fns/init/create-or-modify-npmrc.ts
|
|
1273
|
+
var createOrModifyNpmrc = async ({ quiet = true }, ctx) => {
|
|
1274
|
+
const npmrcPath = Path4.join(ctx.cwd, ".npmrc");
|
|
1275
|
+
if (existsSync(npmrcPath)) {
|
|
1276
|
+
if (!quiet) {
|
|
1277
|
+
console.log(kleur8.yellow("npmrc already exists, not doing anything"));
|
|
1079
1278
|
}
|
|
1279
|
+
} else {
|
|
1280
|
+
writeFileSync3(npmrcPath, getGeneratedNpmrc(ctx));
|
|
1080
1281
|
}
|
|
1081
1282
|
};
|
|
1082
|
-
|
|
1283
|
+
|
|
1284
|
+
// lib/cmd-fns/dev/check-if-initialized.ts
|
|
1285
|
+
import kleur9 from "kleur";
|
|
1286
|
+
import * as Path5 from "path";
|
|
1287
|
+
import {existsSync as existsSync2, readFileSync as readFileSync4} from "fs";
|
|
1288
|
+
var checkIfInitialized = async (ctx) => {
|
|
1289
|
+
const packageJsonPath = Path5.join(ctx.cwd, "package.json");
|
|
1290
|
+
if (!existsSync2(packageJsonPath)) {
|
|
1291
|
+
console.error(kleur9.red(`No package.json found`));
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1294
|
+
const packageJsonRaw = readFileSync4(packageJsonPath, "utf-8");
|
|
1295
|
+
if (!packageJsonRaw.includes("tscircuit")) {
|
|
1296
|
+
console.error(kleur9.red(`No tscircuit dependencies are installed in this project.`));
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
return true;
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
// lib/cmd-fns/init/index.ts
|
|
1083
1303
|
import {z as z17} from "zod";
|
|
1084
1304
|
import {
|
|
1085
|
-
readFileSync as
|
|
1086
|
-
writeFileSync as
|
|
1087
|
-
existsSync,
|
|
1305
|
+
readFileSync as readFileSync5,
|
|
1306
|
+
writeFileSync as writeFileSync4,
|
|
1307
|
+
existsSync as existsSync3,
|
|
1088
1308
|
mkdirSync as mkdirSync2,
|
|
1089
1309
|
appendFileSync
|
|
1090
1310
|
} from "fs";
|
|
1091
|
-
import * as
|
|
1092
|
-
import $
|
|
1311
|
+
import * as Path6 from "node:path";
|
|
1312
|
+
import $3 from "dax-sh";
|
|
1313
|
+
|
|
1314
|
+
// lib/cmd-fns/init/get-generated-tsconfig.ts
|
|
1315
|
+
var getGeneratedTsconfig = () => `
|
|
1316
|
+
|
|
1317
|
+
{
|
|
1318
|
+
"compilerOptions": {
|
|
1319
|
+
// Enable latest features
|
|
1320
|
+
"lib": ["ESNext"],
|
|
1321
|
+
"target": "ESNext",
|
|
1322
|
+
"module": "ESNext",
|
|
1323
|
+
"moduleDetection": "force",
|
|
1324
|
+
"jsx": "react-jsx",
|
|
1325
|
+
"allowJs": true,
|
|
1326
|
+
"types": ["@tscircuit/react-fiber"],
|
|
1327
|
+
"baseUrl": ".",
|
|
1328
|
+
|
|
1329
|
+
// Bundler mode
|
|
1330
|
+
"moduleResolution": "bundler",
|
|
1331
|
+
"allowImportingTsExtensions": true,
|
|
1332
|
+
"verbatimModuleSyntax": true,
|
|
1333
|
+
"noEmit": true,
|
|
1334
|
+
|
|
1335
|
+
// Best practices
|
|
1336
|
+
"strict": true,
|
|
1337
|
+
"skipLibCheck": true,
|
|
1338
|
+
"noFallthroughCasesInSwitch": true,
|
|
1339
|
+
|
|
1340
|
+
// Some stricter flags (disabled by default)
|
|
1341
|
+
"noUnusedLocals": false,
|
|
1342
|
+
"noUnusedParameters": false,
|
|
1343
|
+
"noPropertyAccessFromIndexSignature": false
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
`.trim();
|
|
1348
|
+
|
|
1349
|
+
// lib/cmd-fns/init/index.ts
|
|
1093
1350
|
var initCmd = async (ctx, args) => {
|
|
1094
1351
|
const params = z17.object({
|
|
1095
1352
|
name: z17.string().optional(),
|
|
@@ -1101,9 +1358,17 @@ var initCmd = async (ctx, args) => {
|
|
|
1101
1358
|
} else if (!params.dir) {
|
|
1102
1359
|
params.dir = `.`;
|
|
1103
1360
|
}
|
|
1361
|
+
if (!params.name) {
|
|
1362
|
+
try {
|
|
1363
|
+
const myAccount = await ctx.axios.get("/accounts/get").then((r) => r.data.account);
|
|
1364
|
+
params.name = `@${myAccount.github_username}/${Path6.basename(params.dir)}`;
|
|
1365
|
+
} catch (e) {
|
|
1366
|
+
params.name = Path6.basename(params.dir ?? ctx.cwd);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1104
1369
|
let runtime = params.runtime;
|
|
1105
1370
|
if (!runtime) {
|
|
1106
|
-
const bunExists = $
|
|
1371
|
+
const bunExists = $3.commandExistsSync("bun");
|
|
1107
1372
|
if (bunExists) {
|
|
1108
1373
|
runtime = "bun";
|
|
1109
1374
|
} else {
|
|
@@ -1112,8 +1377,8 @@ var initCmd = async (ctx, args) => {
|
|
|
1112
1377
|
}
|
|
1113
1378
|
const pkm = runtime === "bun" ? "bun" : "npm";
|
|
1114
1379
|
const runner = runtime === "bun" ? "bun" : "tsx";
|
|
1115
|
-
const packageJsonExists =
|
|
1116
|
-
const dirExists =
|
|
1380
|
+
const packageJsonExists = existsSync3(Path6.join(params.dir, "package.json"));
|
|
1381
|
+
const dirExists = existsSync3(params.dir);
|
|
1117
1382
|
if (!dirExists) {
|
|
1118
1383
|
console.log(`Creating directory ${params.dir}`);
|
|
1119
1384
|
mkdirSync2(params.dir, { recursive: true });
|
|
@@ -1121,26 +1386,28 @@ var initCmd = async (ctx, args) => {
|
|
|
1121
1386
|
process.chdir(params.dir);
|
|
1122
1387
|
if (!packageJsonExists) {
|
|
1123
1388
|
console.log(`Initializing ${pkm} project...`);
|
|
1124
|
-
await $
|
|
1389
|
+
await $3`${pkm} init -y`;
|
|
1125
1390
|
}
|
|
1126
1391
|
if (runtime !== "bun") {
|
|
1127
1392
|
console.log(`Setting up project for typescript...`);
|
|
1128
|
-
await $
|
|
1393
|
+
await $3`${pkm} add -D typescript tsx`;
|
|
1129
1394
|
}
|
|
1130
|
-
await $
|
|
1395
|
+
await $3`${pkm} add -D @tscircuit/react-fiber @tscircuit/builder`;
|
|
1131
1396
|
console.log("Changing package name...");
|
|
1132
|
-
const packageJson = JSON.parse(
|
|
1397
|
+
const packageJson = JSON.parse(readFileSync5("package.json", "utf-8"));
|
|
1133
1398
|
packageJson.name = params.name;
|
|
1134
|
-
|
|
1399
|
+
writeFileSync4("package.json", JSON.stringify(packageJson, null, 2));
|
|
1135
1400
|
console.log(`Adding ".tscircuit" to .gitignore`);
|
|
1136
|
-
appendFileSync(".gitignore", "\n.tscircuit\n*.__tmp_entrypoint.tsx", {
|
|
1401
|
+
appendFileSync(".gitignore", "\n.tscircuit\n*.__tmp_entrypoint.tsx\ndist", {
|
|
1137
1402
|
encoding: "utf-8",
|
|
1138
1403
|
flag: "a+"
|
|
1139
1404
|
});
|
|
1405
|
+
console.log("Add .npmrc with tscircuit registry...");
|
|
1406
|
+
await createOrModifyNpmrc({ quiet: false }, ctx);
|
|
1140
1407
|
console.log("Creating lib and examples directories...");
|
|
1141
1408
|
mkdirSync2("examples", { recursive: true });
|
|
1142
1409
|
mkdirSync2("lib", { recursive: true });
|
|
1143
|
-
|
|
1410
|
+
writeFileSync4(Path6.join("lib", "MyCircuit.tsx"), `
|
|
1144
1411
|
export const MyCircuit = () => (
|
|
1145
1412
|
<resistor
|
|
1146
1413
|
resistance="10kohm"
|
|
@@ -1149,76 +1416,155 @@ var initCmd = async (ctx, args) => {
|
|
|
1149
1416
|
/>
|
|
1150
1417
|
)
|
|
1151
1418
|
`.trim());
|
|
1152
|
-
|
|
1153
|
-
|
|
1419
|
+
writeFileSync4("index.ts", `export * from "./lib/MyCircuit"`);
|
|
1420
|
+
writeFileSync4(Path6.join("examples", "MyExample.tsx"), `
|
|
1154
1421
|
import { MyCircuit } from "lib/MyCircuit"
|
|
1155
1422
|
|
|
1156
1423
|
export const MyExample = () => (
|
|
1157
1424
|
<MyCircuit />
|
|
1158
1425
|
)
|
|
1159
1426
|
`.trim());
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
## Developing
|
|
1168
|
-
|
|
1169
|
-
You should install an editor like [VS Code](https://code.visualstudio.com/) with a typescript extension. Many web developers already have this.
|
|
1170
|
-
|
|
1171
|
-
Usually, you'll want to develop some named circuits inside of the \`lib\` directory,
|
|
1172
|
-
export them in the \`index.ts\` file, then show how to use them in the \`examples\` directory.
|
|
1173
|
-
|
|
1174
|
-
Any file in \`examples\` will be automatically loaded and appear in the browser preview when you run \`tsck dev\`. It auto-reloads when you make changes, no need to reload the page or re-run the command.
|
|
1175
|
-
|
|
1176
|
-
> [!TIP] Make sure to replace this README with some details about your project and how to use it.
|
|
1177
|
-
|
|
1178
|
-
## Publishing
|
|
1179
|
-
|
|
1180
|
-
After you're satisfied with your project, publish it on the [tscircuit registry](https://registry.tscircuit.com) with \`tsck publish\`
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
`.trim());
|
|
1185
|
-
writeFileSync3("tsconfig.json", `
|
|
1186
|
-
{
|
|
1187
|
-
"compilerOptions": {
|
|
1188
|
-
// Enable latest features
|
|
1189
|
-
"lib": ["ESNext"],
|
|
1190
|
-
"target": "ESNext",
|
|
1191
|
-
"module": "ESNext",
|
|
1192
|
-
"moduleDetection": "force",
|
|
1193
|
-
"jsx": "react-jsx",
|
|
1194
|
-
"allowJs": true,
|
|
1195
|
-
"types": ["@tscircuit/react-fiber"],
|
|
1196
|
-
"baseUrl": ".",
|
|
1197
|
-
|
|
1198
|
-
// Bundler mode
|
|
1199
|
-
"moduleResolution": "bundler",
|
|
1200
|
-
"allowImportingTsExtensions": true,
|
|
1201
|
-
"verbatimModuleSyntax": true,
|
|
1202
|
-
"noEmit": true,
|
|
1203
|
-
|
|
1204
|
-
// Best practices
|
|
1205
|
-
"strict": true,
|
|
1206
|
-
"skipLibCheck": true,
|
|
1207
|
-
"noFallthroughCasesInSwitch": true,
|
|
1427
|
+
writeFileSync4("README.md", getGeneratedReadme({
|
|
1428
|
+
name: params.name,
|
|
1429
|
+
shouldHaveProjectGeneratedNotice: true
|
|
1430
|
+
}));
|
|
1431
|
+
writeFileSync4("tsconfig.json", getGeneratedTsconfig());
|
|
1432
|
+
console.log("Done! Run `tsci dev` to start developing!");
|
|
1433
|
+
};
|
|
1208
1434
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1435
|
+
// lib/cmd-fns/dev/index.ts
|
|
1436
|
+
var devCmd = async (ctx, args) => {
|
|
1437
|
+
const params = z18.object({
|
|
1438
|
+
cwd: z18.string().optional().default(process.cwd()),
|
|
1439
|
+
port: z18.coerce.number().optional().default(3020)
|
|
1440
|
+
}).parse(args);
|
|
1441
|
+
const { cwd, port } = params;
|
|
1442
|
+
const isInitialized = await checkIfInitialized(ctx);
|
|
1443
|
+
if (!isInitialized) {
|
|
1444
|
+
const { confirmInitialize } = await prompts2({
|
|
1445
|
+
type: "confirm",
|
|
1446
|
+
name: "confirmInitialize",
|
|
1447
|
+
message: "Would you like to initialize this project now?",
|
|
1448
|
+
initial: true
|
|
1449
|
+
});
|
|
1450
|
+
if (confirmInitialize) {
|
|
1451
|
+
return initCmd(ctx, {});
|
|
1452
|
+
} else {
|
|
1453
|
+
process.exit(1);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
await createOrModifyNpmrc({ quiet: false }, ctx);
|
|
1457
|
+
unlink3(Path7.join(cwd, ".tscircuit/dev-server.db")).catch(() => {
|
|
1458
|
+
});
|
|
1459
|
+
console.log(kleur10.green(`\n--------------------------------------------\n\nStarting dev server http://localhost:${port}\n\n--------------------------------------------\n\n`));
|
|
1460
|
+
const serverUrl = `http://localhost:${port}`;
|
|
1461
|
+
const devServerAxios = getDevServerAxios({ serverUrl });
|
|
1462
|
+
const server = await startDevServer({ port, devServerAxios });
|
|
1463
|
+
console.log(`Loading examples...`);
|
|
1464
|
+
await uploadExamplesFromDirectory({ devServerAxios, cwd });
|
|
1465
|
+
const watcher = await startWatcher({ cwd, devServerAxios });
|
|
1466
|
+
while (true) {
|
|
1467
|
+
const { action } = await prompts2({
|
|
1468
|
+
type: "select",
|
|
1469
|
+
name: "action",
|
|
1470
|
+
message: "Action:",
|
|
1471
|
+
choices: [
|
|
1472
|
+
{
|
|
1473
|
+
title: "Open in Browser",
|
|
1474
|
+
value: "open-in-browser"
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
title: "Stop Server",
|
|
1478
|
+
value: "stop"
|
|
1479
|
+
}
|
|
1480
|
+
]
|
|
1481
|
+
});
|
|
1482
|
+
if (action === "open-in-browser") {
|
|
1483
|
+
open(serverUrl);
|
|
1484
|
+
} else if (!action || action === "stop") {
|
|
1485
|
+
if (server.stop)
|
|
1486
|
+
server.stop();
|
|
1487
|
+
if (server.close)
|
|
1488
|
+
server.close();
|
|
1489
|
+
watcher.stop();
|
|
1490
|
+
break;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
// lib/cmd-fns/add.ts
|
|
1495
|
+
import kleur11 from "kleur";
|
|
1496
|
+
import {z as z19} from "zod";
|
|
1497
|
+
import $4 from "dax-sh";
|
|
1498
|
+
var addCmd = async (ctx, args) => {
|
|
1499
|
+
const params = z19.object({
|
|
1500
|
+
packages: z19.array(z19.string()),
|
|
1501
|
+
flags: z19.object({ dev: z19.boolean().optional().default(false) }).optional().default({})
|
|
1502
|
+
}).parse(args);
|
|
1503
|
+
params.packages = params.packages.map((p) => p.replace(/^@/, ""));
|
|
1504
|
+
await createOrModifyNpmrc({ quiet: true }, ctx);
|
|
1505
|
+
$4.cd(ctx.cwd);
|
|
1506
|
+
const flagsString = params.flags.dev ? "--dev" : "";
|
|
1507
|
+
const cmd = `npm add ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1508
|
+
console.log(kleur11.gray(`> ${cmd}`));
|
|
1509
|
+
await $4`npm add ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1510
|
+
};
|
|
1511
|
+
// lib/cmd-fns/remove.ts
|
|
1512
|
+
import kleur12 from "kleur";
|
|
1513
|
+
import {z as z20} from "zod";
|
|
1514
|
+
import $5 from "dax-sh";
|
|
1515
|
+
var removeCmd = async (ctx, args) => {
|
|
1516
|
+
const params = z20.object({
|
|
1517
|
+
packages: z20.array(z20.string()),
|
|
1518
|
+
flags: z20.object({}).optional().default({})
|
|
1519
|
+
}).parse(args);
|
|
1520
|
+
params.packages = params.packages.map((p) => p.replace(/^@/, ""));
|
|
1521
|
+
await createOrModifyNpmrc({ quiet: true }, ctx);
|
|
1522
|
+
$5.cd(ctx.cwd);
|
|
1523
|
+
const flagsString = "";
|
|
1524
|
+
const cmd = `npm remove ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1525
|
+
console.log(kleur12.gray(`> ${cmd}`));
|
|
1526
|
+
await $5`npm remove ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1527
|
+
};
|
|
1528
|
+
// lib/cmd-fns/install.ts
|
|
1529
|
+
import kleur13 from "kleur";
|
|
1530
|
+
import {z as z21} from "zod";
|
|
1531
|
+
import $6 from "dax-sh";
|
|
1532
|
+
var installCmd = async (ctx, args) => {
|
|
1533
|
+
const params = z21.object({
|
|
1534
|
+
packages: z21.array(z21.string()),
|
|
1535
|
+
flags: z21.object({ dev: z21.boolean().optional().default(false) }).optional().default({})
|
|
1536
|
+
}).parse(args);
|
|
1537
|
+
params.packages = params.packages.map((p) => p.replace(/^@/, ""));
|
|
1538
|
+
await createOrModifyNpmrc({ quiet: true }, ctx);
|
|
1539
|
+
$6.cd(ctx.cwd);
|
|
1540
|
+
const flagsString = params.flags.dev ? "--dev" : "";
|
|
1541
|
+
const cmd = `npm install ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1542
|
+
console.log(kleur13.gray(`> ${cmd}`));
|
|
1543
|
+
await $6`npm install ${flagsString} ${params.packages.map((p) => `@tsci/${p.replace(/\//, ".")}`).join(" ")}`;
|
|
1544
|
+
};
|
|
1545
|
+
// lib/cmd-fns/uninstall.ts
|
|
1546
|
+
import kleur14 from "kleur";
|
|
1547
|
+
import {z as z23} from "zod";
|
|
1548
|
+
import $7 from "dax-sh";
|
|
1549
|
+
// lib/cmd-fns/dev-server-upload.ts
|
|
1550
|
+
import {z as z25} from "zod";
|
|
1551
|
+
var devServerUpload = async (ctx, args) => {
|
|
1552
|
+
const params = z25.object({
|
|
1553
|
+
dir: z25.string().optional().default(ctx.cwd),
|
|
1554
|
+
port: z25.coerce.number().optional().default(3020),
|
|
1555
|
+
watch: z25.boolean().optional().default(false)
|
|
1556
|
+
}).parse(args);
|
|
1557
|
+
const serverUrl = `http://localhost:${params.port}`;
|
|
1558
|
+
const devServerAxios = getDevServerAxios({ serverUrl });
|
|
1559
|
+
console.log(`Loading examples...`);
|
|
1560
|
+
await uploadExamplesFromDirectory({ devServerAxios, cwd: params.dir });
|
|
1561
|
+
if (params.watch) {
|
|
1562
|
+
const watcher = await startWatcher({ cwd: params.dir, devServerAxios });
|
|
1213
1563
|
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
`.trim());
|
|
1217
|
-
console.log("Done! Run `tsck dev` to start developing!");
|
|
1218
1564
|
};
|
|
1219
1565
|
// lib/get-program.ts
|
|
1220
1566
|
var getProgram = (ctx) => {
|
|
1221
|
-
const cmd = new Command("
|
|
1567
|
+
const cmd = new Command("tsci");
|
|
1222
1568
|
cmd.version(package_default.version);
|
|
1223
1569
|
const authCmd = cmd.command("auth");
|
|
1224
1570
|
authCmd.command("login").action((args) => authLogin(ctx, args));
|
|
@@ -1246,35 +1592,35 @@ var getProgram = (ctx) => {
|
|
|
1246
1592
|
packageFiles.command("get").action((args) => packageFilesGet(ctx, args));
|
|
1247
1593
|
packageFiles.command("download").requiredOption("--package-name-with-version <package_name_with_version>", "Package name and version").requiredOption("--remote-file-path <remote_file_path>", "Remote file path").option("--output <output>", "Output file path (optional), prints to stdout if not provided").action((args) => packageFilesDownload(ctx, args));
|
|
1248
1594
|
packageFiles.command("create").option("-p, --package-release-id <package_release_id>", "Package Release Id").option("--package-name-with-version <package_name_with_version>", "Package name with version e.g. @tscircuit/arduino@1.2.3").requiredOption("--file <file>", "File to upload").action((args) => packageFilesCreate(ctx, args));
|
|
1249
|
-
packageFiles.command("upload-directory").requiredOption("--
|
|
1595
|
+
packageFiles.command("upload-directory").requiredOption("--dir <dir>", "Directory to upload").action((args) => packageFilesUploadDirectory(ctx, args));
|
|
1250
1596
|
const packageExamples = cmd.command("package_examples");
|
|
1251
1597
|
packageExamples.command("list").requiredOption("--package-name-with-version <package_name_with_version>").action((args) => packageExamplesList(ctx, args));
|
|
1252
1598
|
packageExamples.command("get").requiredOption("--package-example-id <package_example_id>").action((args) => packageExamplesGet(ctx, args));
|
|
1253
1599
|
packageExamples.command("create").requiredOption("--package-name-with-version <package_name_with_version>").requiredOption("--file <file>").option("--export <export>", "Name of export to soupify").action((args) => packageExamplesCreate(ctx, args));
|
|
1254
|
-
cmd.command("publish").action((args) => publish(ctx, args));
|
|
1255
|
-
cmd.command("version").action(() => console.log(`
|
|
1600
|
+
cmd.command("publish").option("--increment", "Increase patch version").option("--patch", "Increase patch version").option("--lock", "Lock the release after publishing to prevent changes").action((args) => publish(ctx, args));
|
|
1601
|
+
cmd.command("version").action(() => console.log(`tsci v${package_default.version}`));
|
|
1256
1602
|
cmd.command("login").action((args) => authLogin(ctx, args));
|
|
1257
1603
|
cmd.command("logout").action((args) => authLogout(ctx, args));
|
|
1258
1604
|
cmd.command("soupify").requiredOption("--file <file>", "Input example files").option("--export <export_name>", "Name of export to soupify, if not specified, soupify the default/only export").action((args) => soupifyCmd(ctx, args));
|
|
1259
1605
|
cmd.command("dev").option("--cwd <cwd>", "Current working directory").option("--port <port>", "Port to run dev server on").action((args) => devCmd(ctx, args));
|
|
1260
1606
|
cmd.command("init").option("--name <name>", "Name of the project").option("--runtime <runtime>", "Runtime to use (attempts to bun, otherwise node/tsx)").option("--dir <dir>", "Directory to initialize (defaults to ./<name> or . if name not provided)").action((args) => initCmd(ctx, args));
|
|
1261
|
-
cmd.command("add").action((
|
|
1262
|
-
});
|
|
1263
|
-
cmd.command("
|
|
1264
|
-
});
|
|
1265
|
-
cmd.command("
|
|
1266
|
-
|
|
1607
|
+
cmd.command("add").argument("<packages...>", "Packages to install from registry.tscircuit.com, optionally with version").option("-D, --dev", "Add to devDependencies").action((packages, flags) => addCmd(ctx, { packages, flags }));
|
|
1608
|
+
cmd.command("remove").argument("<packages...>", "Packages to remove").action((packages, flags) => removeCmd(ctx, { packages, flags }));
|
|
1609
|
+
cmd.command("install").argument("<packages...>", "Packages to install from registry.tscircuit.com, optionally with version").option("-D, --dev", "Add to devDependencies").action((packages, flags) => installCmd(ctx, { packages, flags }));
|
|
1610
|
+
cmd.command("uninstall").argument("<packages...>", "Packages to uninstall").action((packages, flags) => installCmd(ctx, { packages, flags }));
|
|
1611
|
+
const devServerCmd = cmd.command("dev-server");
|
|
1612
|
+
devServerCmd.command("upload").option("--dir <dir>", "Directory to upload (defaults to current directory)").option("-w, --watch", "Watch for changes").option("-p, --port", "Port dev server is running on (default: 3020)").action((args) => devServerUpload(ctx, args));
|
|
1267
1613
|
return cmd;
|
|
1268
1614
|
};
|
|
1269
1615
|
|
|
1270
1616
|
// lib/util/create-context-and-run-program.ts
|
|
1271
1617
|
import defaultAxios2 from "axios";
|
|
1272
|
-
import
|
|
1618
|
+
import kleur15 from "kleur";
|
|
1273
1619
|
|
|
1274
1620
|
// lib/param-handlers/interact-for-local-directory.ts
|
|
1275
|
-
import * as
|
|
1276
|
-
var interactForLocalDirectory = async ({ prompts:
|
|
1277
|
-
const { selectionMode } = await
|
|
1621
|
+
import * as fs6 from "fs/promises";
|
|
1622
|
+
var interactForLocalDirectory = async ({ prompts: prompts3 }) => {
|
|
1623
|
+
const { selectionMode } = await prompts3({
|
|
1278
1624
|
type: "select",
|
|
1279
1625
|
name: "selectionMode",
|
|
1280
1626
|
message: "Select a directory",
|
|
@@ -1284,7 +1630,7 @@ var interactForLocalDirectory = async ({ prompts: prompts2 }) => {
|
|
|
1284
1630
|
]
|
|
1285
1631
|
});
|
|
1286
1632
|
if (selectionMode === "manual") {
|
|
1287
|
-
const { manualPath } = await
|
|
1633
|
+
const { manualPath } = await prompts3({
|
|
1288
1634
|
type: "text",
|
|
1289
1635
|
name: "manualPath",
|
|
1290
1636
|
message: "Enter the path to the directory"
|
|
@@ -1293,9 +1639,9 @@ var interactForLocalDirectory = async ({ prompts: prompts2 }) => {
|
|
|
1293
1639
|
}
|
|
1294
1640
|
let currentDirectory = ".";
|
|
1295
1641
|
while (true) {
|
|
1296
|
-
const files = await
|
|
1642
|
+
const files = await fs6.readdir(currentDirectory);
|
|
1297
1643
|
const dirChoices = files.filter((d) => !d.includes(".")).map((file) => ({ title: file, value: file }));
|
|
1298
|
-
const { selectedDir } = await
|
|
1644
|
+
const { selectedDir } = await prompts3({
|
|
1299
1645
|
type: "autocomplete",
|
|
1300
1646
|
name: "selectedDir",
|
|
1301
1647
|
message: "Select a directory",
|
|
@@ -1312,7 +1658,7 @@ var interactForLocalDirectory = async ({ prompts: prompts2 }) => {
|
|
|
1312
1658
|
} else if (selectedDir === "__select__") {
|
|
1313
1659
|
return currentDirectory;
|
|
1314
1660
|
} else {
|
|
1315
|
-
const stats = await
|
|
1661
|
+
const stats = await fs6.stat(`${currentDirectory}/${selectedDir}`);
|
|
1316
1662
|
if (stats.isFile()) {
|
|
1317
1663
|
throw new Error("Selected file is a file, not a directory");
|
|
1318
1664
|
} else {
|
|
@@ -1323,9 +1669,9 @@ var interactForLocalDirectory = async ({ prompts: prompts2 }) => {
|
|
|
1323
1669
|
};
|
|
1324
1670
|
|
|
1325
1671
|
// lib/param-handlers/interact-for-local-file.ts
|
|
1326
|
-
import * as
|
|
1327
|
-
var interactForLocalFile = async ({ prompts:
|
|
1328
|
-
const { selectionMode } = await
|
|
1672
|
+
import * as fs7 from "fs/promises";
|
|
1673
|
+
var interactForLocalFile = async ({ prompts: prompts3 }) => {
|
|
1674
|
+
const { selectionMode } = await prompts3({
|
|
1329
1675
|
type: "select",
|
|
1330
1676
|
name: "selectionMode",
|
|
1331
1677
|
message: "Select a file",
|
|
@@ -1335,7 +1681,7 @@ var interactForLocalFile = async ({ prompts: prompts2 }) => {
|
|
|
1335
1681
|
]
|
|
1336
1682
|
});
|
|
1337
1683
|
if (selectionMode === "manual") {
|
|
1338
|
-
const { manualPath } = await
|
|
1684
|
+
const { manualPath } = await prompts3({
|
|
1339
1685
|
type: "text",
|
|
1340
1686
|
name: "manualPath",
|
|
1341
1687
|
message: "Enter the path to the file"
|
|
@@ -1344,8 +1690,8 @@ var interactForLocalFile = async ({ prompts: prompts2 }) => {
|
|
|
1344
1690
|
}
|
|
1345
1691
|
let currentDirectory = ".";
|
|
1346
1692
|
while (true) {
|
|
1347
|
-
const files = await
|
|
1348
|
-
const { selectedFile } = await
|
|
1693
|
+
const files = await fs7.readdir(currentDirectory);
|
|
1694
|
+
const { selectedFile } = await prompts3({
|
|
1349
1695
|
type: "autocomplete",
|
|
1350
1696
|
name: "selectedFile",
|
|
1351
1697
|
message: "Select a file or directory",
|
|
@@ -1358,7 +1704,7 @@ var interactForLocalFile = async ({ prompts: prompts2 }) => {
|
|
|
1358
1704
|
const lastIndex = currentDirectory.lastIndexOf("/");
|
|
1359
1705
|
currentDirectory = currentDirectory.substring(0, lastIndex);
|
|
1360
1706
|
} else {
|
|
1361
|
-
const stats = await
|
|
1707
|
+
const stats = await fs7.stat(`${currentDirectory}/${selectedFile}`);
|
|
1362
1708
|
if (stats.isFile()) {
|
|
1363
1709
|
return `${currentDirectory}/${selectedFile}`;
|
|
1364
1710
|
} else {
|
|
@@ -1370,13 +1716,13 @@ var interactForLocalFile = async ({ prompts: prompts2 }) => {
|
|
|
1370
1716
|
|
|
1371
1717
|
// lib/param-handlers/interact-for-package-name-with-version.ts
|
|
1372
1718
|
var interactForPackageNameWithVersion = async ({
|
|
1373
|
-
prompts:
|
|
1719
|
+
prompts: prompts3,
|
|
1374
1720
|
ctx
|
|
1375
1721
|
}) => {
|
|
1376
1722
|
const myPackages = await ctx.axios.post("/packages/list", {
|
|
1377
1723
|
is_writable: true
|
|
1378
1724
|
}).then((r) => r.data.packages);
|
|
1379
|
-
const { selectedPackage } = await
|
|
1725
|
+
const { selectedPackage } = await prompts3({
|
|
1380
1726
|
type: "autocomplete",
|
|
1381
1727
|
name: "selectedPackage",
|
|
1382
1728
|
message: "Select a package",
|
|
@@ -1398,7 +1744,7 @@ var interactForPackageNameWithVersion = async ({
|
|
|
1398
1744
|
]
|
|
1399
1745
|
});
|
|
1400
1746
|
if (selectedPackage.endsWith("@...")) {
|
|
1401
|
-
const { version } = await
|
|
1747
|
+
const { version } = await prompts3({
|
|
1402
1748
|
type: "text",
|
|
1403
1749
|
name: "version",
|
|
1404
1750
|
message: "Enter the version"
|
|
@@ -1406,7 +1752,7 @@ var interactForPackageNameWithVersion = async ({
|
|
|
1406
1752
|
return selectedPackage.replace("@...", `@${version}`);
|
|
1407
1753
|
}
|
|
1408
1754
|
if (selectedPackage === "Enter Manually") {
|
|
1409
|
-
const { packageName } = await
|
|
1755
|
+
const { packageName } = await prompts3({
|
|
1410
1756
|
type: "text",
|
|
1411
1757
|
name: "packageName",
|
|
1412
1758
|
message: "Enter the package name"
|
|
@@ -1418,7 +1764,7 @@ var interactForPackageNameWithVersion = async ({
|
|
|
1418
1764
|
|
|
1419
1765
|
// lib/param-handlers/interact-for-package-release-id.ts
|
|
1420
1766
|
var interactForPackageReleaseId = async (params) => {
|
|
1421
|
-
const { ctx, prompts:
|
|
1767
|
+
const { ctx, prompts: prompts3 } = params;
|
|
1422
1768
|
const package_name_with_version = await interactForPackageNameWithVersion(params);
|
|
1423
1769
|
return await ctx.axios.post("/package_releases/get", {
|
|
1424
1770
|
package_name_with_version
|
|
@@ -1427,12 +1773,12 @@ var interactForPackageReleaseId = async (params) => {
|
|
|
1427
1773
|
|
|
1428
1774
|
// lib/param-handlers/interact-for-package-example-id.ts
|
|
1429
1775
|
var interactForPackageExampleId = async (params) => {
|
|
1430
|
-
const { prompts:
|
|
1776
|
+
const { prompts: prompts3, ctx } = params;
|
|
1431
1777
|
const package_release_id = await interactForPackageReleaseId(params);
|
|
1432
1778
|
const package_examples = await ctx.axios.post("/package_examples/list", {
|
|
1433
1779
|
package_release_id
|
|
1434
1780
|
}).then((r) => r.data.package_examples);
|
|
1435
|
-
const { package_example_id } = await
|
|
1781
|
+
const { package_example_id } = await prompts3({
|
|
1436
1782
|
type: "autocomplete",
|
|
1437
1783
|
name: "package_example_id",
|
|
1438
1784
|
message: "Select a package example",
|
|
@@ -1446,13 +1792,13 @@ var interactForPackageExampleId = async (params) => {
|
|
|
1446
1792
|
|
|
1447
1793
|
// lib/param-handlers/interact-for-package-name.ts
|
|
1448
1794
|
var interactForPackageName = async ({
|
|
1449
|
-
prompts:
|
|
1795
|
+
prompts: prompts3,
|
|
1450
1796
|
ctx
|
|
1451
1797
|
}) => {
|
|
1452
1798
|
const myPackages = await ctx.axios.post("/packages/list", {
|
|
1453
1799
|
is_writable: true
|
|
1454
1800
|
}).then((r) => r.data.packages);
|
|
1455
|
-
const { selectedPackage } = await
|
|
1801
|
+
const { selectedPackage } = await prompts3({
|
|
1456
1802
|
type: "autocomplete",
|
|
1457
1803
|
name: "selectedPackage",
|
|
1458
1804
|
message: "Select a package",
|
|
@@ -1468,7 +1814,7 @@ var interactForPackageName = async ({
|
|
|
1468
1814
|
]
|
|
1469
1815
|
});
|
|
1470
1816
|
if (selectedPackage === "Enter Manually") {
|
|
1471
|
-
const { packageName } = await
|
|
1817
|
+
const { packageName } = await prompts3({
|
|
1472
1818
|
type: "text",
|
|
1473
1819
|
name: "packageName",
|
|
1474
1820
|
message: "Enter the package name"
|
|
@@ -1492,7 +1838,7 @@ var PARAM_HANDLERS_BY_PARAM_NAME = {
|
|
|
1492
1838
|
// lib/util/create-context-and-run-program.ts
|
|
1493
1839
|
var createContextAndRunProgram = async (process_args) => {
|
|
1494
1840
|
const args = minimist(process_args);
|
|
1495
|
-
const global_config = new Configstore("
|
|
1841
|
+
const global_config = new Configstore("tsci");
|
|
1496
1842
|
const current_profile = args.profile ?? global_config.get("current_profile") ?? "default";
|
|
1497
1843
|
const profile_config = {
|
|
1498
1844
|
get: (key) => global_config.get(`profiles.${current_profile}.${key}`),
|
|
@@ -1515,17 +1861,17 @@ var createContextAndRunProgram = async (process_args) => {
|
|
|
1515
1861
|
if (global_config.get("log_requests")) {
|
|
1516
1862
|
console.log(`Using registry_url: ${registry_url}`);
|
|
1517
1863
|
axios.interceptors.request.use((req) => {
|
|
1518
|
-
console.log(
|
|
1864
|
+
console.log(kleur15.grey(`[REQ] ${req.method?.toUpperCase()} ${req.url}`));
|
|
1519
1865
|
return req;
|
|
1520
1866
|
});
|
|
1521
1867
|
axios.interceptors.response.use((res) => {
|
|
1522
|
-
console.log(
|
|
1868
|
+
console.log(kleur15.grey(`[RES] ${res.status} ${res.config.method?.toUpperCase()} ${res.config.url}`));
|
|
1523
1869
|
return res;
|
|
1524
1870
|
});
|
|
1525
1871
|
}
|
|
1526
1872
|
axios.interceptors.response.use((res) => res, (err) => {
|
|
1527
|
-
console.log(
|
|
1528
|
-
console.log(
|
|
1873
|
+
console.log(kleur15.red(`[ERR] ${err.response?.status} ${err.config.method?.toUpperCase()} ${err.config.url}\n\n${JSON.stringify(err.response?.data, null, " ")}`.replace(/\\n/g, "\n").replace(/\\"/g, '"')));
|
|
1874
|
+
console.log(kleur15.yellow("[Request Body]:"), err.config.data);
|
|
1529
1875
|
return Promise.reject(err);
|
|
1530
1876
|
});
|
|
1531
1877
|
const ctx = {
|
|
@@ -1544,10 +1890,10 @@ var createContextAndRunProgram = async (process_args) => {
|
|
|
1544
1890
|
params: args
|
|
1545
1891
|
};
|
|
1546
1892
|
await perfectCli(getProgram(ctx), process.argv, {
|
|
1547
|
-
async customParamHandler({ commandPath, optionName }, { prompts:
|
|
1893
|
+
async customParamHandler({ commandPath, optionName }, { prompts: prompts3 }) {
|
|
1548
1894
|
const optionNameHandler = PARAM_HANDLERS_BY_PARAM_NAME[_.snakeCase(optionName)];
|
|
1549
1895
|
if (optionNameHandler) {
|
|
1550
|
-
return optionNameHandler({ prompts:
|
|
1896
|
+
return optionNameHandler({ prompts: prompts3, ctx });
|
|
1551
1897
|
}
|
|
1552
1898
|
}
|
|
1553
1899
|
});
|