@nestia/editor 12.0.0-rc.3 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/assets/index-D7-O5u45.js +4140 -0
  2. package/dist/index.html +1 -1
  3. package/lib/NestiaEditorApplication.mjs +66 -3
  4. package/lib/NestiaEditorApplication.mjs.map +1 -1
  5. package/lib/NestiaEditorIframe.js +11 -17
  6. package/lib/NestiaEditorIframe.js.map +1 -1
  7. package/lib/NestiaEditorIframe.mjs +159 -3
  8. package/lib/NestiaEditorIframe.mjs.map +1 -1
  9. package/lib/NestiaEditorModule.mjs +51 -3
  10. package/lib/NestiaEditorModule.mjs.map +1 -1
  11. package/lib/NestiaEditorUploader.js +4 -11
  12. package/lib/NestiaEditorUploader.js.map +1 -1
  13. package/lib/NestiaEditorUploader.mjs +129 -3
  14. package/lib/NestiaEditorUploader.mjs.map +1 -1
  15. package/lib/index.mjs +4 -2
  16. package/lib/internal/NestiaEditorArchiver.d.ts +20 -0
  17. package/lib/internal/NestiaEditorArchiver.js +38 -0
  18. package/lib/internal/NestiaEditorArchiver.js.map +1 -0
  19. package/lib/internal/NestiaEditorArchiver.mjs +24 -0
  20. package/lib/internal/NestiaEditorArchiver.mjs.map +1 -0
  21. package/lib/internal/NestiaEditorComposer.mjs +52 -3
  22. package/lib/internal/NestiaEditorComposer.mjs.map +1 -1
  23. package/lib/internal/NestiaEditorFileUploader.mjs +43 -3
  24. package/lib/internal/NestiaEditorFileUploader.mjs.map +1 -1
  25. package/lib/main.mjs +9 -3
  26. package/lib/main.mjs.map +1 -1
  27. package/package.json +8 -11
  28. package/src/NestiaEditorIframe.tsx +35 -22
  29. package/src/NestiaEditorUploader.tsx +6 -14
  30. package/src/internal/NestiaEditorArchiver.ts +43 -0
  31. package/dist/assets/index-CkZ2qgCJ.js +0 -4310
  32. package/lib/NestiaEditorApplication2.mjs +0 -86
  33. package/lib/NestiaEditorApplication2.mjs.map +0 -1
  34. package/lib/NestiaEditorIframe2.mjs +0 -139
  35. package/lib/NestiaEditorIframe2.mjs.map +0 -1
  36. package/lib/NestiaEditorModule2.mjs +0 -124
  37. package/lib/NestiaEditorModule2.mjs.map +0 -1
  38. package/lib/NestiaEditorUploader2.mjs +0 -90
  39. package/lib/NestiaEditorUploader2.mjs.map +0 -1
  40. package/lib/_virtual/NestiaEditorApplication.mjs +0 -4
  41. package/lib/_virtual/NestiaEditorApplication.mjs.map +0 -1
  42. package/lib/_virtual/NestiaEditorComposer.mjs +0 -4
  43. package/lib/_virtual/NestiaEditorComposer.mjs.map +0 -1
  44. package/lib/_virtual/NestiaEditorFileUploader.mjs +0 -4
  45. package/lib/_virtual/NestiaEditorFileUploader.mjs.map +0 -1
  46. package/lib/_virtual/NestiaEditorIframe.mjs +0 -4
  47. package/lib/_virtual/NestiaEditorIframe.mjs.map +0 -1
  48. package/lib/_virtual/NestiaEditorModule.mjs +0 -4
  49. package/lib/_virtual/NestiaEditorModule.mjs.map +0 -1
  50. package/lib/_virtual/NestiaEditorUploader.mjs +0 -4
  51. package/lib/_virtual/NestiaEditorUploader.mjs.map +0 -1
  52. package/lib/_virtual/_commonjsHelpers.mjs +0 -35
  53. package/lib/_virtual/_commonjsHelpers.mjs.map +0 -1
  54. package/lib/_virtual/_js-yaml.mjs +0 -7
  55. package/lib/_virtual/_js-yaml.mjs.map +0 -1
  56. package/lib/_virtual/_node_fs.mjs +0 -7
  57. package/lib/_virtual/_node_fs.mjs.map +0 -1
  58. package/lib/_virtual/_react-mui-fileuploader.mjs +0 -7
  59. package/lib/_virtual/_react-mui-fileuploader.mjs.map +0 -1
  60. package/lib/_virtual/_react.mjs +0 -7
  61. package/lib/_virtual/_react.mjs.map +0 -1
  62. package/lib/_virtual/client.mjs +0 -7
  63. package/lib/_virtual/client.mjs.map +0 -1
  64. package/lib/_virtual/estree.mjs +0 -7
  65. package/lib/_virtual/estree.mjs.map +0 -1
  66. package/lib/_virtual/index.mjs +0 -4
  67. package/lib/_virtual/index.mjs.map +0 -1
  68. package/lib/_virtual/jsx-runtime.mjs +0 -7
  69. package/lib/_virtual/jsx-runtime.mjs.map +0 -1
  70. package/lib/_virtual/main.mjs +0 -4
  71. package/lib/_virtual/main.mjs.map +0 -1
  72. package/lib/_virtual/material.mjs +0 -7
  73. package/lib/_virtual/material.mjs.map +0 -1
  74. package/lib/_virtual/migrate.mjs +0 -7
  75. package/lib/_virtual/migrate.mjs.map +0 -1
  76. package/lib/_virtual/sdk.mjs +0 -7
  77. package/lib/_virtual/sdk.mjs.map +0 -1
  78. package/lib/_virtual/standalone.mjs +0 -7
  79. package/lib/_virtual/standalone.mjs.map +0 -1
  80. package/lib/_virtual/typescript.mjs +0 -7
  81. package/lib/_virtual/typescript.mjs.map +0 -1
  82. package/lib/index.mjs.map +0 -1
  83. package/lib/index2.mjs +0 -35
  84. package/lib/index2.mjs.map +0 -1
  85. package/lib/internal/NestiaEditorComposer2.mjs +0 -110
  86. package/lib/internal/NestiaEditorComposer2.mjs.map +0 -1
  87. package/lib/internal/NestiaEditorFileUploader2.mjs +0 -51
  88. package/lib/internal/NestiaEditorFileUploader2.mjs.map +0 -1
  89. package/lib/main2.mjs +0 -16
  90. package/lib/main2.mjs.map +0 -1
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Archives a composed project into a zip file and hands it to the browser.
3
+ *
4
+ * StackBlitz can no longer run nestia projects: the generated projects build
5
+ * through `ttsc`, whose TypeScript compiler is a native Go binary that a
6
+ * WebContainer cannot execute. The editor therefore delivers the composed
7
+ * project as a downloadable zip archive instead of an embedded StackBlitz
8
+ * workspace.
9
+ */
10
+ export declare namespace NestiaEditorArchiver {
11
+ /** Pack the composed project files into a zip archive. */
12
+ const pack: (files: Record<string, string>) => Uint8Array;
13
+ /** Compose a safe archive file name from a package name. */
14
+ const name: (packageName: string) => string;
15
+ /** Trigger a browser download of the packed archive. */
16
+ const download: (props: {
17
+ name: string;
18
+ files: Record<string, string>;
19
+ }) => void;
20
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NestiaEditorArchiver = void 0;
4
+ const fflate_1 = require("fflate");
5
+ /**
6
+ * Archives a composed project into a zip file and hands it to the browser.
7
+ *
8
+ * StackBlitz can no longer run nestia projects: the generated projects build
9
+ * through `ttsc`, whose TypeScript compiler is a native Go binary that a
10
+ * WebContainer cannot execute. The editor therefore delivers the composed
11
+ * project as a downloadable zip archive instead of an embedded StackBlitz
12
+ * workspace.
13
+ */
14
+ var NestiaEditorArchiver;
15
+ (function (NestiaEditorArchiver) {
16
+ /** Pack the composed project files into a zip archive. */
17
+ NestiaEditorArchiver.pack = (files) => {
18
+ const entries = {};
19
+ for (const [key, value] of Object.entries(files))
20
+ entries[key] = (0, fflate_1.strToU8)(value);
21
+ return (0, fflate_1.zipSync)(entries);
22
+ };
23
+ /** Compose a safe archive file name from a package name. */
24
+ NestiaEditorArchiver.name = (packageName) => `${packageName.replace(/^@/, "").replace(/[^A-Za-z0-9._-]+/g, "-")}.zip`;
25
+ /** Trigger a browser download of the packed archive. */
26
+ NestiaEditorArchiver.download = (props) => {
27
+ const blob = new Blob([NestiaEditorArchiver.pack(props.files)], {
28
+ type: "application/zip",
29
+ });
30
+ const url = URL.createObjectURL(blob);
31
+ const anchor = window.document.createElement("a");
32
+ anchor.href = url;
33
+ anchor.download = props.name;
34
+ anchor.click();
35
+ setTimeout(() => URL.revokeObjectURL(url), 30000);
36
+ };
37
+ })(NestiaEditorArchiver || (exports.NestiaEditorArchiver = NestiaEditorArchiver = {}));
38
+ //# sourceMappingURL=NestiaEditorArchiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NestiaEditorArchiver.js","sourceRoot":"","sources":["../../src/internal/NestiaEditorArchiver.ts"],"names":[],"mappings":";;;AAAA,mCAA0C;AAE1C;;;;;;;;GAQG;AACH,IAAiB,oBAAoB,CA+BpC;AA/BD,WAAiB,oBAAoB;IACnC,0DAA0D;IAC7C,yBAAI,GAAG,CAAC,KAA6B,EAAc,EAAE;QAChE,MAAM,OAAO,GAA+B,EAAE,CAAC;QAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,GAAG,IAAA,gBAAO,EAAC,KAAK,CAAC,CAAC;QAChC,OAAO,IAAA,gBAAO,EAAC,OAAO,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,4DAA4D;IAC/C,yBAAI,GAAG,CAAC,WAAmB,EAAU,EAAE,CAClD,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC;IAE3E,wDAAwD;IAC3C,6BAAQ,GAAG,CAAC,KAGxB,EAAQ,EAAE;QACT,MAAM,IAAI,GAAS,IAAI,IAAI,CACzB,CAAC,yBAAI,CAAC,KAAK,CAAC,KAAK,CAA4B,CAAC,EAC9C;YACE,IAAI,EAAE,iBAAiB;SACxB,CACF,CAAC;QACF,MAAM,GAAG,GAAW,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAsB,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC;QAClB,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;QAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,KAAM,CAAC,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC,EA/BgB,oBAAoB,aAApB,oBAAoB,GAApB,oBAAoB,QA+BpC"}
@@ -0,0 +1,24 @@
1
+ import { strToU8, zipSync } from "fflate";
2
+ //#region src/internal/NestiaEditorArchiver.ts
3
+ let NestiaEditorArchiver;
4
+ (function(_NestiaEditorArchiver) {
5
+ const pack = _NestiaEditorArchiver.pack = (files) => {
6
+ const entries = {};
7
+ for (const [key, value] of Object.entries(files)) entries[key] = strToU8(value);
8
+ return zipSync(entries);
9
+ };
10
+ _NestiaEditorArchiver.name = (packageName) => `${packageName.replace(/^@/, "").replace(/[^A-Za-z0-9._-]+/g, "-")}.zip`;
11
+ _NestiaEditorArchiver.download = (props) => {
12
+ const blob = new Blob([pack(props.files)], { type: "application/zip" });
13
+ const url = URL.createObjectURL(blob);
14
+ const anchor = window.document.createElement("a");
15
+ anchor.href = url;
16
+ anchor.download = props.name;
17
+ anchor.click();
18
+ setTimeout(() => URL.revokeObjectURL(url), 3e4);
19
+ };
20
+ })(NestiaEditorArchiver || (NestiaEditorArchiver = {}));
21
+ //#endregion
22
+ export { NestiaEditorArchiver };
23
+
24
+ //# sourceMappingURL=NestiaEditorArchiver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NestiaEditorArchiver.mjs","names":[],"sources":[],"mappings":""}
@@ -1,3 +1,52 @@
1
- export { NestiaEditorComposer } from './NestiaEditorComposer2.mjs';
2
- export { __exports as default } from '../_virtual/NestiaEditorComposer.mjs';
3
- //# sourceMappingURL=NestiaEditorComposer.mjs.map
1
+ import migrate from "@nestia/migrate";
2
+ import * as prettierEsTreePlugin from "prettier/plugins/estree";
3
+ import * as prettierTsPlugin from "prettier/plugins/typescript";
4
+ import { format } from "prettier/standalone";
5
+ //#region src/internal/NestiaEditorComposer.ts
6
+ let NestiaEditorComposer;
7
+ (function(_NestiaEditorComposer) {
8
+ _NestiaEditorComposer.nest = (props) => compose({
9
+ openFile: "README.md,test/start.ts",
10
+ startScript: ["build:test,test", ""],
11
+ migrate: (app) => app.nest(props)
12
+ })(props);
13
+ _NestiaEditorComposer.sdk = async (props) => compose({
14
+ openFile: "README.md,test/start.ts",
15
+ startScript: ["swagger", "hello"],
16
+ migrate: (app) => app.sdk(props)
17
+ })(props);
18
+ const compose = (config) => async (props) => {
19
+ if (props.files !== void 0) return {
20
+ success: true,
21
+ data: {
22
+ files: props.files,
23
+ openFile: config.openFile,
24
+ startScript: config.startScript
25
+ }
26
+ };
27
+ const result = await migrate.NestiaMigrateApplication.validate(props.document);
28
+ if (result.success === false) return result;
29
+ const app = result.data;
30
+ const files = config.migrate(app);
31
+ for (const [key, value] of Object.entries(files)) if (key.substring(key.length - 3) === ".ts") try {
32
+ files[key] = await format(value, {
33
+ parser: "typescript",
34
+ plugins: [prettierEsTreePlugin, prettierTsPlugin]
35
+ });
36
+ } catch (exp) {
37
+ console.log(exp);
38
+ }
39
+ return {
40
+ success: true,
41
+ data: {
42
+ files,
43
+ openFile: config.openFile,
44
+ startScript: config.startScript
45
+ }
46
+ };
47
+ };
48
+ })(NestiaEditorComposer || (NestiaEditorComposer = {}));
49
+ //#endregion
50
+ export { NestiaEditorComposer };
51
+
52
+ //# sourceMappingURL=NestiaEditorComposer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"NestiaEditorComposer.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"NestiaEditorComposer.mjs","names":[],"sources":[],"mappings":""}
@@ -1,3 +1,43 @@
1
- export { NestiaEditorFileUploader } from './NestiaEditorFileUploader2.mjs';
2
- export { __exports as default } from '../_virtual/NestiaEditorFileUploader.mjs';
3
- //# sourceMappingURL=NestiaEditorFileUploader.mjs.map
1
+ import React from "react";
2
+ import { load } from "js-yaml";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import FileUpload from "react-mui-fileuploader";
5
+ //#region src/internal/NestiaEditorFileUploader.tsx
6
+ function NestiaEditorFileUploader(props) {
7
+ const [elements, setElements] = React.useState([]);
8
+ const onChange = async (array) => {
9
+ if (array.length === 0) {
10
+ props.onChange(null, null);
11
+ return;
12
+ }
13
+ const file = array[array.length - 1];
14
+ const buffer = await file.arrayBuffer();
15
+ const content = new TextDecoder().decode(buffer);
16
+ const extension = file.name.split(".").pop();
17
+ try {
18
+ const json = extension === "json" ? JSON.parse(content) : load(content);
19
+ props.onChange(json, null);
20
+ } catch {
21
+ props.onChange(null, extension === "json" ? "Invalid JSON file" : "Invalid YAML file");
22
+ return;
23
+ }
24
+ if (array.length > 1) setElements([file]);
25
+ };
26
+ return /* @__PURE__ */ jsx(FileUpload, {
27
+ defaultFiles: elements,
28
+ onFilesChange: onChange,
29
+ acceptedType: ".json, .yaml",
30
+ getBase64: false,
31
+ multiFile: false,
32
+ maxUploadFiles: 1,
33
+ title: "Swagger file uploader",
34
+ header: "Drag and drop a Swagger file here",
35
+ buttonLabel: "Click Here",
36
+ rightLabel: "to select swagger.json/yaml file",
37
+ buttonRemoveLabel: "Clear"
38
+ });
39
+ }
40
+ //#endregion
41
+ export { NestiaEditorFileUploader };
42
+
43
+ //# sourceMappingURL=NestiaEditorFileUploader.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"NestiaEditorFileUploader.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"NestiaEditorFileUploader.mjs","names":[],"sources":[],"mappings":""}
package/lib/main.mjs CHANGED
@@ -1,3 +1,9 @@
1
- import './main2.mjs';
2
- export { __exports as default } from './_virtual/main.mjs';
3
- //# sourceMappingURL=main.mjs.map
1
+ import { NestiaEditorApplication } from "./NestiaEditorApplication.mjs";
2
+ import { StrictMode } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { createRoot } from "react-dom/client";
5
+ //#region src/main.tsx
6
+ createRoot(document.getElementById("root")).render(/* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(NestiaEditorApplication, {}) }));
7
+ //#endregion
8
+
9
+ //# sourceMappingURL=main.mjs.map
package/lib/main.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"main.mjs","names":[],"sources":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestia/editor",
3
- "version": "12.0.0-rc.3",
3
+ "version": "12.0.0",
4
4
  "main": "lib/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -38,13 +38,13 @@
38
38
  "homepage": "https://nestia.io",
39
39
  "dependencies": {
40
40
  "@mui/material": "^5.15.6",
41
- "@stackblitz/sdk": "^1.11.0",
42
- "@typia/interface": "^13.0.0",
41
+ "@typia/interface": "^13.0.2",
42
+ "fflate": "^0.8.2",
43
43
  "js-yaml": "^4.1.0",
44
44
  "prettier": "^3.8.1",
45
45
  "react-mui-fileuploader": "^0.5.2",
46
- "typia": "^13.0.0",
47
- "@nestia/migrate": "^12.0.0-rc.3"
46
+ "typia": "^13.0.2",
47
+ "@nestia/migrate": "^12.0.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@eslint/js": "^9.13.0",
@@ -52,9 +52,7 @@
52
52
  "@nestjs/core": "^11.1.6",
53
53
  "@nestjs/platform-express": "^11.1.6",
54
54
  "@nestjs/platform-fastify": "^11.1.6",
55
- "@rollup/plugin-commonjs": "^29.0.0",
56
- "@rollup/plugin-esm-shim": "^0.1.8",
57
- "@rollup/plugin-node-resolve": "^16.0.3",
55
+ "@ttsc/unplugin": "^0.18.4",
58
56
  "@types/js-yaml": "^4.0.9",
59
57
  "@types/node": "^25.3.3",
60
58
  "@types/react": "^18.3.11",
@@ -66,8 +64,7 @@
66
64
  "globals": "^15.11.0",
67
65
  "react": "^18.3.1",
68
66
  "react-dom": "^18.3.1",
69
- "rollup": "^4.56.0",
70
- "rollup-plugin-node-externals": "^8.1.2",
67
+ "rolldown": "^1.1.5",
71
68
  "typescript": "^7.0.2",
72
69
  "vite": "^5.4.9"
73
70
  },
@@ -85,7 +82,7 @@
85
82
  "scripts": {
86
83
  "build": "npm run build:static && npm run build:lib",
87
84
  "build:static": "rimraf dist && ttsc && vite build",
88
- "build:lib": "rimraf lib && ttsc --project tsconfig.lib.json && tsc --project tsconfig.lib.json --emitDeclarationOnly && rollup -c",
85
+ "build:lib": "rimraf lib && ttsc --project tsconfig.lib.json && tsc --project tsconfig.lib.json --emitDeclarationOnly && rolldown -c && node ../../deploy/verify-package-exports.cjs --esm-static",
89
86
  "dev": "vite",
90
87
  "lint": "eslint .",
91
88
  "preview": "vite preview"
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  Alert,
3
3
  AlertTitle,
4
+ Button,
4
5
  CircularProgress,
5
6
  Step,
6
7
  StepContent,
@@ -8,24 +9,25 @@ import {
8
9
  Stepper,
9
10
  Typography,
10
11
  } from "@mui/material";
11
- import StackBlitzSDK from "@stackblitz/sdk";
12
12
  import { OpenApiV3, OpenApiV3_1, SwaggerV2 } from "@typia/interface";
13
13
  import { load } from "js-yaml";
14
14
  import React from "react";
15
15
  import { IValidation } from "typia";
16
16
 
17
+ import { NestiaEditorArchiver } from "./internal/NestiaEditorArchiver";
17
18
  import { NestiaEditorComposer } from "./internal/NestiaEditorComposer";
18
19
 
19
20
  export function NestiaEditorIframe(props: NestiaEditorIframe.IProps) {
20
- const [id] = React.useState(
21
- `reactia-editor-div-${Math.random().toString().substring(2)}`,
22
- );
23
21
  const [step, setStep] = React.useState(0);
24
22
  const [fetchError, setFetchError] = React.useState<string | null>(null);
25
23
  const [operations, setOperationCount] = React.useState<
26
24
  Record<string, number>
27
25
  >({});
28
26
  const [composerError, setComposerError] = React.useState<any | null>(null);
27
+ const [files, setFiles] = React.useState<Record<string, string> | null>(null);
28
+ const archive: string = NestiaEditorArchiver.name(
29
+ props.package ?? "@ORGANIZATION/PROJECT",
30
+ );
29
31
 
30
32
  React.useEffect(() => {
31
33
  (async () => {
@@ -69,29 +71,15 @@ export function NestiaEditorIframe(props: NestiaEditorIframe.IProps) {
69
71
  return;
70
72
  }
71
73
 
72
- // COMPOSING STACKBLITZ PROJECT
74
+ // READY TO DOWNLOAD
73
75
  setStep(2);
74
- StackBlitzSDK.embedProject(
75
- id,
76
- {
77
- title: document.info?.title ?? "Nestia Editor",
78
- template: "node",
79
- files: result.data.files,
80
- },
81
- {
82
- width: "100%",
83
- height: "100%",
84
- openFile: result.data.openFile,
85
- startScript: result.data.startScript as any, // no problem
86
- },
87
- );
76
+ setFiles(result.data.files);
88
77
  })().catch((exp) => {
89
78
  console.error("unknown error", exp);
90
79
  });
91
80
  }, []);
92
81
  return (
93
82
  <div
94
- id={id}
95
83
  style={{
96
84
  width: "100%",
97
85
  height: "100%",
@@ -176,9 +164,34 @@ export function NestiaEditorIframe(props: NestiaEditorIframe.IProps) {
176
164
  </Step>
177
165
  <Step key={2}>
178
166
  <StepLabel>
179
- <Typography variant="h5">Composing TypeScript Project</Typography>
167
+ <Typography variant="h5">
168
+ Downloading TypeScript Project
169
+ </Typography>
180
170
  </StepLabel>
181
- <StepContent></StepContent>
171
+ <StepContent>
172
+ <br />
173
+ {files !== null ? (
174
+ <>
175
+ <p>
176
+ {Object.keys(files).length.toLocaleString()} files are
177
+ ready.
178
+ </p>
179
+ <Button
180
+ variant="contained"
181
+ color="success"
182
+ size="large"
183
+ onClick={() =>
184
+ NestiaEditorArchiver.download({
185
+ name: archive,
186
+ files,
187
+ })
188
+ }
189
+ >
190
+ Download {archive}
191
+ </Button>
192
+ </>
193
+ ) : null}
194
+ </StepContent>
182
195
  </Step>
183
196
  </Stepper>
184
197
  </div>
@@ -8,10 +8,10 @@ import {
8
8
  Switch,
9
9
  TextField,
10
10
  } from "@mui/material";
11
- import StackBlitzSDK from "@stackblitz/sdk";
12
11
  import { OpenApiV3, OpenApiV3_1, SwaggerV2 } from "@typia/interface";
13
12
  import React from "react";
14
13
 
14
+ import { NestiaEditorArchiver } from "./internal/NestiaEditorArchiver";
15
15
  import { NestiaEditorComposer } from "./internal/NestiaEditorComposer";
16
16
  import { NestiaEditorFileUploader } from "./internal/NestiaEditorFileUploader";
17
17
 
@@ -58,18 +58,10 @@ export function NestiaEditorUploader(props: NestiaEditorUploader.IProps) {
58
58
  package: name,
59
59
  });
60
60
  if (result.success === true) {
61
- StackBlitzSDK.openProject(
62
- {
63
- title: document.info?.title ?? "Nestia Editor",
64
- template: "node",
65
- files: result.data.files,
66
- },
67
- {
68
- newWindow: true,
69
- openFile: result.data.openFile,
70
- startScript: result.data.startScript as any,
71
- },
72
- );
61
+ NestiaEditorArchiver.download({
62
+ name: NestiaEditorArchiver.name(name),
63
+ files: result.data.files,
64
+ });
73
65
  } else {
74
66
  handleError(JSON.stringify(result.errors, null, 2));
75
67
  }
@@ -142,7 +134,7 @@ export function NestiaEditorUploader(props: NestiaEditorUploader.IProps) {
142
134
  disabled={progress === true || document === null}
143
135
  onClick={() => generate()}
144
136
  >
145
- {progress ? "Generating..." : "Generate Editor"}
137
+ {progress ? "Generating..." : "Download Project"}
146
138
  </Button>
147
139
  </>
148
140
  );
@@ -0,0 +1,43 @@
1
+ import { strToU8, zipSync } from "fflate";
2
+
3
+ /**
4
+ * Archives a composed project into a zip file and hands it to the browser.
5
+ *
6
+ * StackBlitz can no longer run nestia projects: the generated projects build
7
+ * through `ttsc`, whose TypeScript compiler is a native Go binary that a
8
+ * WebContainer cannot execute. The editor therefore delivers the composed
9
+ * project as a downloadable zip archive instead of an embedded StackBlitz
10
+ * workspace.
11
+ */
12
+ export namespace NestiaEditorArchiver {
13
+ /** Pack the composed project files into a zip archive. */
14
+ export const pack = (files: Record<string, string>): Uint8Array => {
15
+ const entries: Record<string, Uint8Array> = {};
16
+ for (const [key, value] of Object.entries(files))
17
+ entries[key] = strToU8(value);
18
+ return zipSync(entries);
19
+ };
20
+
21
+ /** Compose a safe archive file name from a package name. */
22
+ export const name = (packageName: string): string =>
23
+ `${packageName.replace(/^@/, "").replace(/[^A-Za-z0-9._-]+/g, "-")}.zip`;
24
+
25
+ /** Trigger a browser download of the packed archive. */
26
+ export const download = (props: {
27
+ name: string;
28
+ files: Record<string, string>;
29
+ }): void => {
30
+ const blob: Blob = new Blob(
31
+ [pack(props.files) as Uint8Array<ArrayBuffer>],
32
+ {
33
+ type: "application/zip",
34
+ },
35
+ );
36
+ const url: string = URL.createObjectURL(blob);
37
+ const anchor: HTMLAnchorElement = window.document.createElement("a");
38
+ anchor.href = url;
39
+ anchor.download = props.name;
40
+ anchor.click();
41
+ setTimeout(() => URL.revokeObjectURL(url), 30_000);
42
+ };
43
+ }