@sealcode/sealgen 0.15.22 → 0.15.24

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 (36) hide show
  1. package/@types/src/add-collection.d.ts +1 -1
  2. package/@types/src/page/list.d.ts +1 -0
  3. package/@types/src/page/sealious-list.d.ts +1 -1
  4. package/@types/src/page/sealious-list.test.d.ts +1 -0
  5. package/@types/src/test_utils/exec_async.d.ts +1 -0
  6. package/@types/src/test_utils/test-on-real-app.d.ts +24 -0
  7. package/@types/src/utils/input-wrapper.d.ts +1 -1
  8. package/lib/add-collection.js +18 -8
  9. package/lib/add-route.test.js +9 -92
  10. package/lib/forms/controls/checkboxed-list-input.js +31 -22
  11. package/lib/forms/controls/checkboxed-list-input.test.js +173 -0
  12. package/lib/forms/controls/multiple-files.js +1 -0
  13. package/lib/forms/controls/simple-input.js +1 -0
  14. package/lib/forms/controls/tickable.js +1 -0
  15. package/lib/forms/fields/field.js +1 -1
  16. package/lib/page/list.js +1 -0
  17. package/lib/page/sealious-list.js +5 -2
  18. package/lib/page/sealious-list.test.js +24 -0
  19. package/lib/test_utils/exec_async.js +24 -0
  20. package/lib/test_utils/test-on-real-app.js +147 -0
  21. package/lib/utils/input-wrapper.js +4 -2
  22. package/package.json +1 -1
  23. package/src/add-collection.ts +23 -8
  24. package/src/add-route.test.ts +9 -99
  25. package/src/forms/controls/checkboxed-list-input.test.ts +189 -0
  26. package/src/forms/controls/checkboxed-list-input.ts +36 -23
  27. package/src/forms/controls/multiple-files.ts +1 -0
  28. package/src/forms/controls/simple-input.ts +1 -0
  29. package/src/forms/controls/tickable.ts +1 -0
  30. package/src/forms/fields/field.ts +3 -1
  31. package/src/page/list.ts +1 -1
  32. package/src/page/sealious-list.test.ts +25 -0
  33. package/src/page/sealious-list.ts +7 -3
  34. package/src/test_utils/exec_async.ts +26 -0
  35. package/src/test_utils/test-on-real-app.ts +167 -0
  36. package/src/utils/input-wrapper.ts +4 -1
@@ -1 +1 @@
1
- export declare function addCollection(): Promise<void>;
1
+ export declare function addCollection(params?: Record<string, string | boolean>): Promise<void>;
@@ -9,6 +9,7 @@ import { SimpleFormField } from "../forms/fields/simple-form-field.js";
9
9
  import { FormDataValue } from "../forms/form-types.js";
10
10
  export declare const BasePagePropsShape: {};
11
11
  export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
12
+ export declare const DEFAULT_ITEMS_PER_PAGE = 12;
12
13
  export declare const BaseListPageFields: {
13
14
  readonly page: NumberField<1, false>;
14
15
  readonly itemsPerPage: NumberField<12, false>;
@@ -9,7 +9,7 @@ export declare abstract class SealiousItemListPage<C extends Collection, F exten
9
9
  getTotalPages(ctx: Context, itemsPerPage: number): Promise<number>;
10
10
  protected addFilter(ctx: Context, list: ItemList<C>): Promise<void>;
11
11
  makeQuery(ctx: Context): Promise<ItemList<C>>;
12
- getItems(ctx: Context, page: number, itemsPerPage: number): Promise<CollectionItem<C>[]>;
12
+ getItems(ctx: Context, page: number, itemsPerPage: number | null): Promise<CollectionItem<C>[]>;
13
13
  getFilterValues(ctx: Context): Promise<Record<string, string | undefined>>;
14
14
  renderItem(_: Context, item: CollectionItem<C>, _index: number): Promise<FlatTemplatable>;
15
15
  canAccess: (ctx: Context) => Promise<{
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const execAsync: (cmd: string) => Promise<void>;
@@ -0,0 +1,24 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import EventEmitter from "events";
3
+ export declare const callSealgenBin: () => string;
4
+ export declare class RealAppTest {
5
+ node_pid: number;
6
+ app_port: number;
7
+ mongo_port: number;
8
+ app_path: string;
9
+ container_name: string;
10
+ emitter: EventEmitter;
11
+ static init(): Promise<RealAppTest>;
12
+ start(): Promise<void>;
13
+ close(): Promise<void>;
14
+ runSealgenCommand(command: string): Promise<void>;
15
+ throwIfItKillsTheApp<T>(cb: () => Promise<T>): Promise<T>;
16
+ httpGet(path: string): Promise<{
17
+ status: number;
18
+ response: string;
19
+ }>;
20
+ httpPOST(path: string, body: Record<string, unknown>): Promise<{
21
+ status: number;
22
+ response: string;
23
+ }>;
24
+ }
@@ -2,4 +2,4 @@
2
2
  import { Readable } from "stream";
3
3
  import { FlatTemplatable } from "tempstream";
4
4
  export declare function getRequiredClass(isRequired: boolean): string;
5
- export declare function inputWrapper(modifiers: string[], input: FlatTemplatable): Readable;
5
+ export declare function inputWrapper(type: string, modifiers: string[], input: FlatTemplatable): Readable;
@@ -1,18 +1,28 @@
1
1
  import { resolve } from "path";
2
2
  import _locreq from "locreq";
3
3
  import { promises as fs, existsSync } from "fs";
4
- import { question } from "./utils/question.js";
5
4
  import { collectionTemplate } from "./templates/collection.js";
6
5
  import { generateCollections } from "./generate-collections.js";
7
6
  import { toKebabCase, toPascalCase } from "js-convert-case";
8
7
  const target_locreq = _locreq(process.cwd());
9
- async function addCollection() {
10
- const collection_name = toPascalCase(
11
- await question(
12
- "What's the name of the collection class: ",
13
- (s) => s.length > 3
14
- )
15
- );
8
+ import prompts from "prompts";
9
+ async function addCollection(params = {}) {
10
+ prompts.override(params);
11
+ const response = await prompts([
12
+ {
13
+ type: "text",
14
+ name: "collection_name",
15
+ message: "What's the name of the collection class: ",
16
+ validate: (s) => s.length > 3 ? true : "Should be at least 3 characters long",
17
+ format: function(action) {
18
+ return toPascalCase(action);
19
+ }
20
+ }
21
+ ]);
22
+ const collection_name = response.collection_name;
23
+ if (!collection_name) {
24
+ throw new Error("Empty collection name, please try again");
25
+ }
16
26
  const file_path = target_locreq.resolve(
17
27
  `src/back/collections/${toKebabCase(collection_name)}.ts`
18
28
  );
@@ -1,98 +1,15 @@
1
- import { promises as fs } from "fs";
2
- import { exec, spawn } from "child_process";
3
- import axios from "axios";
4
- import waitPort from "wait-port";
5
- import getPort from "get-port";
6
- import _locreq from "locreq";
7
- import { module_dirname } from "./utils/module-dirname.js";
8
- const locreq = _locreq(module_dirname(import.meta.url));
9
- const execAsync = (cmd) => {
10
- return new Promise((resolve, reject) => {
11
- const process2 = exec(cmd);
12
- console.log("EXECUTING", cmd);
13
- process2?.stdout?.on("data", (data) => {
14
- console.log(`stdout: ${data}`);
15
- });
16
- process2?.stderr?.on("data", (data) => {
17
- console.log(`stdout: ${data}`);
18
- });
19
- process2.on("exit", (code) => {
20
- if (code == 0) {
21
- resolve();
22
- } else {
23
- reject();
24
- }
25
- });
26
- });
27
- };
28
- const callSealgenBin = () => `node ${locreq.resolve("lib/cli.js")}`;
1
+ import { RealAppTest } from "./test_utils/test-on-real-app.js";
29
2
  describe("add route test", () => {
30
- let node_pid = 0;
31
- const timestamp = Date.now();
32
- const app_path = `/tmp/sealious-minimal-${timestamp}`;
33
- const container_name = `sealgen-test-mongo-${timestamp}`;
34
- afterEach(async function() {
35
- try {
36
- if (node_pid != 0)
37
- await execAsync(`kill -9 ${node_pid}`);
38
- } catch (error) {
39
- throw new Error("node process exited unexpectedly, please inspect the output");
40
- }
41
- await Promise.all([
42
- fs.rm(app_path, { recursive: true, force: true }),
43
- execAsync(`docker rm -f ${container_name}`)
44
- ]);
45
- });
46
- it("creates minimal sealious app", async function() {
47
- console.log(" cloning sealious-minimal...");
48
- await execAsync(
49
- `git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout df8cccf`
50
- );
51
- console.log(" installing dependencies and sealgen...");
52
- await execAsync(
53
- `cd ${app_path} && npm ci rm -rf node_modules/@sealcode/sealgen && ln -s $OLDPWD node_modules/@sealcode/sealgen && ${callSealgenBin()} make-env`
54
- );
55
- console.log(" running add-route and building app...");
56
- await execAsync(
57
- `cd ${app_path} && ${callSealgenBin()} add-route --url=/sample --action=Sample --mode=page && ${callSealgenBin()} build`
58
- );
59
- console.log(
60
- " starting the database...(if timeouts here, run 'docker pull mongo:4.4-bionic' to solve)"
61
- );
62
- const mongo_port = await getPort();
63
- const app_port = await getPort();
64
- await execAsync(
65
- `docker run -p ${mongo_port}:27017 --name ${container_name} -d mongo:4.4-bionic`
66
- );
67
- console.log(" running app and making request to the generated url...");
68
- const env = {
69
- ...process.env,
70
- SEALIOUS_MONGO_PORT: mongo_port.toString(),
71
- SEALIOUS_PORT: app_port.toString()
72
- };
73
- const node_args = [`${app_path}/dist/back/index.js`];
74
- console.log(
75
- "EXECUTING APP",
76
- `${Object.entries(env).map(([key, value]) => `${key}=${value}`).join(" ")} node ${node_args.join(" ")}`
77
- );
78
- let app_start = spawn(`node`, node_args, {
79
- env
80
- });
81
- app_start.stdout.on("data", (data) => console.log("APP OUTPUT:", data.toString()));
82
- app_start.stderr.on("data", (data) => console.log(data.toString()));
83
- if (app_start.pid == void 0)
84
- throw new Error("node failed to start");
85
- node_pid = app_start.pid;
86
- await waitPort({
87
- host: "localhost",
88
- port: app_port,
89
- output: "silent",
90
- timeout: 6e3
91
- });
92
- const res = await axios.get(`http://localhost:${app_port}/sample`);
3
+ it("creates minimal sealious app and adds a simple route that works", async function() {
4
+ const test_app = await RealAppTest.init();
5
+ const path = "/sample";
6
+ await test_app.runSealgenCommand(`add-route --url=${path} --action=Sample --mode=page`);
7
+ await test_app.start();
8
+ const res = await test_app.httpGet(path);
93
9
  if (res.status != 200) {
94
- throw new Error("url generated by add-route exists but doesnt return 200");
10
+ throw new Error("url generated by add-route exists but doesn't return 200");
95
11
  }
12
+ await test_app.close();
96
13
  }).timeout(100 * 1e3);
97
14
  });
98
15
  //# sourceMappingURL=add-route.test.js.map
@@ -8,34 +8,43 @@ class CheckboxedListInput extends FormFieldControl {
8
8
  this.options = options;
9
9
  }
10
10
  async render(fctx) {
11
- const { parsed: currentValue } = await this.field.getValue(
12
- fctx.ctx,
13
- fctx.data
14
- );
11
+ const {
12
+ valid,
13
+ message,
14
+ parsed: currentValue
15
+ } = await this.field.getValue(fctx.ctx, fctx.data, true);
15
16
  const pickedValues = Object.entries(currentValue || {}).filter(([, is_on]) => is_on).map(([key]) => key);
16
17
  const [options, isVisible] = await Promise.all([
17
18
  this.field.generateOptions(fctx.ctx),
18
19
  this.field.isVisible(fctx.ctx)
19
20
  ]);
20
21
  const { name, required } = this.field;
21
- return tempstream`${isVisible ? options.map(
22
- ({ value, label }) => inputWrapper(
23
- [name, getRequiredClass(required)],
24
- /* HTML */
25
- `
26
- <input
27
- type="checkbox"
28
- id="${name}.${value}"
29
- name="${name}.${value}"
30
- ${pickedValues.includes(value) ? "checked" : ""}
31
- form=${fctx.form_id}
32
- />
33
- <label for="${name}.${value}"
34
- >${label || value}</label
35
- >
36
- `
37
- )
38
- ) : ""}`;
22
+ return inputWrapper(
23
+ "checkboxed-list",
24
+ [name, getRequiredClass(required)],
25
+ tempstream`<label
26
+ >${this.options.label || this.field.name}</label
27
+ >${isVisible ? options.map(
28
+ ({ value, label }) => inputWrapper(
29
+ "checkbox",
30
+ [name],
31
+ /* HTML */
32
+ `
33
+ <input
34
+ type="checkbox"
35
+ id="${name}.${value}"
36
+ name="${name}.${value}"
37
+ ${pickedValues.includes(value) ? "checked" : ""}
38
+ form=${fctx.form_id}
39
+ />
40
+ <label for="${name}.${value}"
41
+ >${label || value}</label
42
+ >
43
+ `
44
+ )
45
+ ) : ""}
46
+ ${~valid && fctx.validate ? `<div class="input__error">${message}</div>` : ""}`
47
+ );
39
48
  }
40
49
  }
41
50
  export {
@@ -7,6 +7,7 @@ import { Form } from "../form.js";
7
7
  import { CheckboxedListInput } from "./checkboxed-list-input.js";
8
8
  import { getBrowser } from "../../utils/browser-creator.js";
9
9
  import assert from "assert";
10
+ import { sleep } from "../../utils/sleep.js";
10
11
  describe("checkboxed-list", () => {
11
12
  describe("basic tests", async () => {
12
13
  let server;
@@ -58,6 +59,178 @@ describe("checkboxed-list", () => {
58
59
  await page.getByRole("button", { name: "Wy\u015Blij" }).click();
59
60
  assert.deepStrictEqual(received_values, { syllables: { co: "on", de: "on" } });
60
61
  });
62
+ describe("behavior with (non)required", () => {
63
+ it("allows to send a form when no value is selected and fiels is not required", async () => {
64
+ const port = await getPort();
65
+ console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
66
+ const app = new Koa();
67
+ const router = new Router();
68
+ const fields = {
69
+ syllables: new CheckboxedListField(false, async () => {
70
+ await sleep(10);
71
+ return ["sa", "mple", "co", "de"].map((s) => ({
72
+ value: s,
73
+ label: s.toLocaleUpperCase()
74
+ }));
75
+ })
76
+ };
77
+ let submit_called = false;
78
+ mount(
79
+ router,
80
+ "/",
81
+ new class extends Form {
82
+ constructor() {
83
+ super(...arguments);
84
+ this.fields = fields;
85
+ this.controls = [new CheckboxedListInput(fields.syllables)];
86
+ }
87
+ async onSubmit(ctx) {
88
+ submit_called = true;
89
+ }
90
+ async canAccess(_) {
91
+ return { canAccess: true, message: "" };
92
+ }
93
+ }(),
94
+ true
95
+ );
96
+ app.use(router.routes()).use(router.allowedMethods());
97
+ server = app.listen(port);
98
+ browser = await getBrowser();
99
+ const context = await browser.newContext();
100
+ page = await context.newPage();
101
+ await page.goto(`http://localhost:${port}`);
102
+ await page.getByRole("button", { name: "Wy\u015Blij" }).click();
103
+ await sleep(100);
104
+ assert(submit_called);
105
+ });
106
+ it("does not allow to send a form when no value is selected and field IS required", async () => {
107
+ const port = await getPort();
108
+ console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
109
+ const app = new Koa();
110
+ const router = new Router();
111
+ const fields = {
112
+ syllables: new CheckboxedListField(true, async () => {
113
+ await sleep(10);
114
+ return ["sa", "mple", "co", "de"].map((s) => ({
115
+ value: s,
116
+ label: s.toLocaleUpperCase()
117
+ }));
118
+ })
119
+ };
120
+ let submit_called = false;
121
+ mount(
122
+ router,
123
+ "/",
124
+ new class extends Form {
125
+ constructor() {
126
+ super(...arguments);
127
+ this.fields = fields;
128
+ this.controls = [new CheckboxedListInput(fields.syllables)];
129
+ }
130
+ async onSubmit(ctx) {
131
+ submit_called = true;
132
+ }
133
+ async canAccess(_) {
134
+ return { canAccess: true, message: "" };
135
+ }
136
+ }(),
137
+ true
138
+ );
139
+ app.use(router.routes()).use(router.allowedMethods());
140
+ server = app.listen(port);
141
+ browser = await getBrowser();
142
+ const context = await browser.newContext();
143
+ page = await context.newPage();
144
+ await page.goto(`http://localhost:${port}`);
145
+ await page.getByRole("button", { name: "Wy\u015Blij" }).click();
146
+ await sleep(100);
147
+ assert(submit_called == false);
148
+ await page.getByText(/empty value/).click({ timeout: 500 });
149
+ });
150
+ it("allows to send a form when no value is selected and field IS required", async () => {
151
+ const port = await getPort();
152
+ console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
153
+ const app = new Koa();
154
+ const router = new Router();
155
+ const fields = {
156
+ syllables: new CheckboxedListField(true, async () => {
157
+ await sleep(10);
158
+ return ["sa", "mple", "co", "de"].map((s) => ({
159
+ value: s,
160
+ label: s.toLocaleUpperCase()
161
+ }));
162
+ })
163
+ };
164
+ let submit_called = false;
165
+ mount(
166
+ router,
167
+ "/",
168
+ new class extends Form {
169
+ constructor() {
170
+ super(...arguments);
171
+ this.fields = fields;
172
+ this.controls = [new CheckboxedListInput(fields.syllables)];
173
+ }
174
+ async onSubmit(ctx) {
175
+ submit_called = true;
176
+ }
177
+ async canAccess(_) {
178
+ return { canAccess: true, message: "" };
179
+ }
180
+ }(),
181
+ true
182
+ );
183
+ app.use(router.routes()).use(router.allowedMethods());
184
+ server = app.listen(port);
185
+ browser = await getBrowser();
186
+ const context = await browser.newContext();
187
+ page = await context.newPage();
188
+ await page.goto(`http://localhost:${port}`);
189
+ await page.getByLabel("DE").check();
190
+ await page.getByRole("button", { name: "Wy\u015Blij" }).click();
191
+ await sleep(100);
192
+ assert(submit_called);
193
+ });
194
+ });
195
+ it("renders the label", async () => {
196
+ const port = await getPort();
197
+ console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
198
+ const app = new Koa();
199
+ const router = new Router();
200
+ const fields = {
201
+ syllables: new CheckboxedListField(true, async () => {
202
+ return ["sa", "mple", "co", "de"].map((s) => ({
203
+ value: s,
204
+ label: s.toLocaleUpperCase()
205
+ }));
206
+ })
207
+ };
208
+ mount(
209
+ router,
210
+ "/",
211
+ new class extends Form {
212
+ constructor() {
213
+ super(...arguments);
214
+ this.fields = fields;
215
+ this.controls = [new CheckboxedListInput(fields.syllables, { label: "ABC" })];
216
+ }
217
+ async onSubmit(ctx) {
218
+ }
219
+ async canAccess(_) {
220
+ return { canAccess: true, message: "" };
221
+ }
222
+ }(),
223
+ true
224
+ );
225
+ app.use(router.routes()).use(router.allowedMethods());
226
+ server = app.listen(port);
227
+ browser = await getBrowser();
228
+ const context = await browser.newContext();
229
+ page = await context.newPage();
230
+ await page.goto(`http://localhost:${port}`);
231
+ await page.getByRole("button", { name: "Wy\u015Blij" }).click();
232
+ await page.getByText(/ABC/).click({ timeout: 500 });
233
+ });
61
234
  });
62
235
  });
63
236
  //# sourceMappingURL=checkboxed-list-input.test.js.map
@@ -161,6 +161,7 @@ class MultipleFiles extends FormFieldControl {
161
161
  ])
162
162
  )).filter(([_, f]) => f !== null);
163
163
  return inputWrapper(
164
+ "multiple-files",
164
165
  ["multiple-files", this.field.name, ...this.getClassModifiers()],
165
166
  tempstream`
166
167
  <label>${this.options.label || this.field.name}</label>
@@ -73,6 +73,7 @@ class SimpleInput extends FormFieldControl {
73
73
  true
74
74
  );
75
75
  return inputWrapper(
76
+ "simple-input",
76
77
  this.getWrapperModifiers(),
77
78
  tempstream`
78
79
  ${label ? `<label for="${id}">${label}</label>` : ""}
@@ -33,6 +33,7 @@ class Tickable extends FormFieldControl {
33
33
  const readonly = this.options.readonly || false;
34
34
  const required = this.field.required;
35
35
  return inputWrapper(
36
+ this.type,
36
37
  this.getWrapperClasses(fctx.ctx, fctx.data, parsed),
37
38
  /* HTML */
38
39
  `
@@ -40,7 +40,7 @@ class FormField {
40
40
  if (raw == void 0 || raw == null || raw == "") {
41
41
  return {
42
42
  parsed: null,
43
- message: this.required ? "empty value" : "",
43
+ message: this.required ? "empty value, but field is required" : "",
44
44
  raw,
45
45
  ...validate ? { valid: !this.required } : {}
46
46
  };
package/lib/page/list.js CHANGED
@@ -202,6 +202,7 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
202
202
  export {
203
203
  BaseListPageFields,
204
204
  BasePagePropsShape,
205
+ DEFAULT_ITEMS_PER_PAGE,
205
206
  ListPage
206
207
  };
207
208
  //# sourceMappingURL=list.js.map
@@ -1,4 +1,7 @@
1
- import { ListPage } from "./list.js";
1
+ import {
2
+ DEFAULT_ITEMS_PER_PAGE,
3
+ ListPage
4
+ } from "./list.js";
2
5
  import { peopleWhoCan } from "./access-control.js";
3
6
  import qs from "qs";
4
7
  import { is, predicates } from "@sealcode/ts-predicates";
@@ -39,7 +42,7 @@ class SealiousItemListPage extends ListPage {
39
42
  async getItems(ctx, page, itemsPerPage) {
40
43
  const query = await this.makeQuery(ctx);
41
44
  await this.addFilter(ctx, query);
42
- const { items } = await query.paginate({ items: itemsPerPage, page }).fetch();
45
+ const { items } = await query.paginate({ items: itemsPerPage || DEFAULT_ITEMS_PER_PAGE, page }).fetch();
43
46
  return items;
44
47
  }
45
48
  async getFilterValues(ctx) {
@@ -0,0 +1,24 @@
1
+ import assert from "assert";
2
+ import { RealAppTest } from "../test_utils/test-on-real-app.js";
3
+ describe("sealious-list test", () => {
4
+ it("creates minimal sealious app and adds a simple route that works", async function() {
5
+ const test_app = await RealAppTest.init();
6
+ const url = "/list";
7
+ await test_app.runSealgenCommand(`add-collection --collection_name=samples`);
8
+ await test_app.runSealgenCommand(
9
+ `add-route --url=${url} --action=List --mode=list --collection=samples`
10
+ );
11
+ await test_app.start();
12
+ const res_post = await test_app.httpPOST("/api/v1/collections/samples", {
13
+ content: "some-content"
14
+ });
15
+ assert(res_post.status == 201);
16
+ const res = await test_app.httpGet(url);
17
+ if (res.status != 200) {
18
+ throw new Error("url generated by add-route exists but doesn't return 200");
19
+ }
20
+ assert(res.response.includes("some-content"));
21
+ await test_app.close();
22
+ }).timeout(100 * 1e3);
23
+ });
24
+ //# sourceMappingURL=sealious-list.test.js.map
@@ -0,0 +1,24 @@
1
+ import { exec } from "child_process";
2
+ const execAsync = (cmd) => {
3
+ return new Promise((resolve, reject) => {
4
+ const process = exec(cmd);
5
+ console.log("EXECUTING", cmd);
6
+ process?.stdout?.on("data", (data) => {
7
+ console.log(`stdout: ${data}`);
8
+ });
9
+ process?.stderr?.on("data", (data) => {
10
+ console.log(`stdout: ${data}`);
11
+ });
12
+ process.on("exit", (code) => {
13
+ if (code == 0) {
14
+ resolve();
15
+ } else {
16
+ reject();
17
+ }
18
+ });
19
+ });
20
+ };
21
+ export {
22
+ execAsync
23
+ };
24
+ //# sourceMappingURL=exec_async.js.map