@sealcode/sealgen 0.15.2 → 0.15.4

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 (200) hide show
  1. package/.eslintrc.cjs +38 -0
  2. package/.xunit +53 -0
  3. package/@types/src/page/stateful-page.d.ts +4 -2
  4. package/build.js +1 -1
  5. package/{dist → lib}/add-route.test.js +2 -2
  6. package/{dist → lib}/build.js +3 -3
  7. package/{dist → lib}/builder.js +3 -0
  8. package/lib/cli.js +39 -0
  9. package/{dist → lib}/esbuild-plugins/load-assets.js +2 -2
  10. package/{dist → lib}/forms/controls/array.js +1 -2
  11. package/{dist → lib}/forms/controls/autocomplete.js +8 -10
  12. package/{dist → lib}/forms/controls/checkboxed-list-input.test.js +3 -2
  13. package/{dist → lib}/forms/controls/collapsible-field-group.js +2 -1
  14. package/{dist → lib}/forms/controls/editable-collection-subset.js +6 -4
  15. package/{dist → lib}/forms/controls/field-group.js +1 -1
  16. package/{dist → lib}/forms/controls/file.js +2 -5
  17. package/{dist → lib}/forms/controls/form-control.js +2 -1
  18. package/{dist → lib}/forms/controls/photo.js +1 -1
  19. package/{dist → lib}/forms/controls/photo.test.js +5 -7
  20. package/{dist → lib}/forms/controls/textarea.js +1 -1
  21. package/{dist → lib}/forms/fields/boolean.test.js +0 -1
  22. package/{dist → lib}/forms/fields/field.js +2 -5
  23. package/{dist → lib}/forms/fields/file.js +8 -2
  24. package/{dist → lib}/forms/fields/structured-array.js +1 -1
  25. package/{dist → lib}/forms/form.test.js +8 -9
  26. package/{dist → lib}/forms/multiform.test.js +14 -14
  27. package/{dist → lib}/generate-components.js +1 -1
  28. package/{dist → lib}/generate-routes.test.js +1 -0
  29. package/{dist → lib}/generate-stimulus.js +1 -1
  30. package/{dist → lib}/mount.js +4 -3
  31. package/{dist → lib}/notifier.js +3 -1
  32. package/{dist → lib}/page/list.js +1 -1
  33. package/{dist → lib}/page/mountable-with-fields.js +1 -1
  34. package/{dist → lib}/page/sealious-list.js +2 -2
  35. package/{dist → lib}/page/stateful-page.js +34 -17
  36. package/{dist → lib}/templates/jdd-component.js +2 -2
  37. package/{dist → lib}/utils/extract-fields-from-collection.js +16 -1
  38. package/package.json +20 -15
  39. package/src/add-route.test.ts +2 -2
  40. package/src/add-route.ts +2 -2
  41. package/src/build.ts +3 -3
  42. package/src/builder.ts +3 -2
  43. package/src/esbuild-plugins/load-assets.ts +5 -4
  44. package/src/esbuild-plugins/rewrite-asset-imports.ts +4 -3
  45. package/src/forms/controls/array.ts +5 -3
  46. package/src/forms/controls/autocomplete.ts +2 -2
  47. package/src/forms/controls/checkbox-group.ts +1 -5
  48. package/src/forms/controls/checkboxed-list-input.test.ts +3 -2
  49. package/src/forms/controls/checkboxed-list-input.ts +0 -1
  50. package/src/forms/controls/collapsible-field-group.ts +4 -2
  51. package/src/forms/controls/derived.ts +1 -1
  52. package/src/forms/controls/dropdown.ts +3 -3
  53. package/src/forms/controls/editable-collection-subset.ts +10 -4
  54. package/src/forms/controls/field-group.ts +1 -1
  55. package/src/forms/controls/file.ts +1 -1
  56. package/src/forms/controls/form-control.ts +2 -1
  57. package/src/forms/controls/hidden-input.ts +0 -2
  58. package/src/forms/controls/multiple-files.ts +8 -5
  59. package/src/forms/controls/number.ts +1 -1
  60. package/src/forms/controls/photo.test.ts +6 -7
  61. package/src/forms/controls/photo.ts +3 -6
  62. package/src/forms/controls/simple-input.ts +3 -5
  63. package/src/forms/controls/textarea.ts +3 -8
  64. package/src/forms/controls/tickable.ts +2 -4
  65. package/src/forms/fields/boolean.test.ts +0 -1
  66. package/src/forms/fields/existing-sealious-value.ts +1 -0
  67. package/src/forms/fields/field.ts +5 -8
  68. package/src/forms/fields/file.ts +13 -6
  69. package/src/forms/fields/structured-array.ts +2 -2
  70. package/src/forms/form.test.ts +9 -10
  71. package/src/forms/form.ts +1 -1
  72. package/src/forms/multiform.test.ts +14 -14
  73. package/src/forms/multiform.ts +2 -1
  74. package/src/generate-components.ts +1 -2
  75. package/src/generate-routes.test.ts +1 -0
  76. package/src/generate-stimulus.ts +1 -1
  77. package/src/get-fonts.ts +2 -0
  78. package/src/mount.ts +8 -3
  79. package/src/notifier.ts +3 -1
  80. package/src/page/list.ts +1 -1
  81. package/src/page/mountable-with-fields.ts +3 -1
  82. package/src/page/sealious-list.ts +3 -2
  83. package/src/page/stateful-page.ts +50 -22
  84. package/src/templates/jdd-component.ts +2 -2
  85. package/src/utils/extract-fields-from-collection.ts +19 -1
  86. package/src/utils/translate.ts +1 -1
  87. package/src/utils/utils.ts +2 -0
  88. package/test.cjs +54 -0
  89. package/tsconfig.json +3 -4
  90. package/dist/back/routes/sample.page.js +0 -17
  91. package/dist/back/routes/sample.test.js +0 -39
  92. package/dist/hehe.test.js +0 -7
  93. package/dist/sample-index.js +0 -3
  94. package/dist/sample-lib.js +0 -9
  95. package/dist/vitest.config.js +0 -23
  96. package/src/vitest.config.ts +0 -21
  97. /package/{dist → lib}/add-collection.js +0 -0
  98. /package/{dist → lib}/add-jdd-component.js +0 -0
  99. /package/{dist → lib}/add-route.js +0 -0
  100. /package/{dist → lib}/components/components.js +0 -0
  101. /package/{dist → lib}/components/tabs.js +0 -0
  102. /package/{dist → lib}/components/toggle-modal.js +0 -0
  103. /package/{dist → lib}/embeddable-file-extensions.js +0 -0
  104. /package/{dist → lib}/esbuild-plugins/rewrite-asset-imports.js +0 -0
  105. /package/{dist → lib}/example.test.js +0 -0
  106. /package/{dist → lib}/find-css-includes.js +0 -0
  107. /package/{dist → lib}/forms/controls/big-radio-group.js +0 -0
  108. /package/{dist → lib}/forms/controls/checkbox-group.js +0 -0
  109. /package/{dist → lib}/forms/controls/checkbox.js +0 -0
  110. /package/{dist → lib}/forms/controls/checkboxed-list-input.js +0 -0
  111. /package/{dist → lib}/forms/controls/collapsible-checkbox-group.js +0 -0
  112. /package/{dist → lib}/forms/controls/controls.js +0 -0
  113. /package/{dist → lib}/forms/controls/derived-frame.js +0 -0
  114. /package/{dist → lib}/forms/controls/derived.js +0 -0
  115. /package/{dist → lib}/forms/controls/dropdown.js +0 -0
  116. /package/{dist → lib}/forms/controls/form-field-control.js +0 -0
  117. /package/{dist → lib}/forms/controls/form-header.js +0 -0
  118. /package/{dist → lib}/forms/controls/form-paragraph.js +0 -0
  119. /package/{dist → lib}/forms/controls/frame.js +0 -0
  120. /package/{dist → lib}/forms/controls/hidden-input.js +0 -0
  121. /package/{dist → lib}/forms/controls/html.js +0 -0
  122. /package/{dist → lib}/forms/controls/multiple-files.js +0 -0
  123. /package/{dist → lib}/forms/controls/multiple-photos.js +0 -0
  124. /package/{dist → lib}/forms/controls/number.js +0 -0
  125. /package/{dist → lib}/forms/controls/radio.js +0 -0
  126. /package/{dist → lib}/forms/controls/reverse-single-reference.js +0 -0
  127. /package/{dist → lib}/forms/controls/simple-input.js +0 -0
  128. /package/{dist → lib}/forms/controls/tickable.js +0 -0
  129. /package/{dist → lib}/forms/fields/boolean.js +0 -0
  130. /package/{dist → lib}/forms/fields/checkboxed-list.js +0 -0
  131. /package/{dist → lib}/forms/fields/collection-field.js +0 -0
  132. /package/{dist → lib}/forms/fields/email.js +0 -0
  133. /package/{dist → lib}/forms/fields/email.test.js +0 -0
  134. /package/{dist → lib}/forms/fields/enum-multiple.js +0 -0
  135. /package/{dist → lib}/forms/fields/enum-multiple.test.js +0 -0
  136. /package/{dist → lib}/forms/fields/enum.js +0 -0
  137. /package/{dist → lib}/forms/fields/enum.test.js +0 -0
  138. /package/{dist → lib}/forms/fields/existing-sealious-value.js +0 -0
  139. /package/{dist → lib}/forms/fields/fields.js +0 -0
  140. /package/{dist → lib}/forms/fields/multiple-files.js +0 -0
  141. /package/{dist → lib}/forms/fields/number.js +0 -0
  142. /package/{dist → lib}/forms/fields/number.test.js +0 -0
  143. /package/{dist → lib}/forms/fields/pick-from-list.js +0 -0
  144. /package/{dist → lib}/forms/fields/pick-from-list.test.js +0 -0
  145. /package/{dist → lib}/forms/fields/proxy-field.js +0 -0
  146. /package/{dist → lib}/forms/fields/regexp.js +0 -0
  147. /package/{dist → lib}/forms/fields/regexp.test.js +0 -0
  148. /package/{dist → lib}/forms/fields/reverse-single-reference.js +0 -0
  149. /package/{dist → lib}/forms/fields/simple-form-field.js +0 -0
  150. /package/{dist → lib}/forms/form-types.js +0 -0
  151. /package/{dist → lib}/forms/form.js +0 -0
  152. /package/{dist → lib}/forms/multiform.js +0 -0
  153. /package/{dist → lib}/generate-collections.js +0 -0
  154. /package/{dist → lib}/generate-routes.js +0 -0
  155. /package/{dist → lib}/get-fonts.js +0 -0
  156. /package/{dist → lib}/index.js +0 -0
  157. /package/{dist → lib}/koa.d.js +0 -0
  158. /package/{dist → lib}/make-env.js +0 -0
  159. /package/{dist → lib}/make-hidden-inputs.js +0 -0
  160. /package/{dist → lib}/page/access-control.js +0 -0
  161. /package/{dist → lib}/page/default-list-filters.js +0 -0
  162. /package/{dist → lib}/page/mountable.js +0 -0
  163. /package/{dist → lib}/page/page.js +0 -0
  164. /package/{dist → lib}/page/props-parser.js +0 -0
  165. /package/{dist → lib}/run.js +0 -0
  166. /package/{dist → lib}/sanitize.js +0 -0
  167. /package/{dist → lib}/target-locreq.js +0 -0
  168. /package/{dist → lib}/templates/collection.js +0 -0
  169. /package/{dist → lib}/templates/form.js +0 -0
  170. /package/{dist → lib}/templates/list.js +0 -0
  171. /package/{dist → lib}/templates/long-running-process.js +0 -0
  172. /package/{dist → lib}/templates/multiform.js +0 -0
  173. /package/{dist → lib}/templates/page.js +0 -0
  174. /package/{dist → lib}/templates/post.js +0 -0
  175. /package/{dist → lib}/templates/redirect.js +0 -0
  176. /package/{dist → lib}/templates/route-test.js +0 -0
  177. /package/{dist → lib}/templates/stateful-page.js +0 -0
  178. /package/{dist → lib}/templates/stimulus-controller.js +0 -0
  179. /package/{dist → lib}/templates/templates.js +0 -0
  180. /package/{dist → lib}/test_utils/locator_is_visible.js +0 -0
  181. /package/{dist → lib}/utils/assert-throws.js +0 -0
  182. /package/{dist → lib}/utils/base64.js +0 -0
  183. /package/{dist → lib}/utils/browser-creator.js +0 -0
  184. /package/{dist → lib}/utils/escape-url-params.js +0 -0
  185. /package/{dist → lib}/utils/exec.js +0 -0
  186. /package/{dist → lib}/utils/format-external-url.js +0 -0
  187. /package/{dist → lib}/utils/import-path.js +0 -0
  188. /package/{dist → lib}/utils/input-wrapper.js +0 -0
  189. /package/{dist → lib}/utils/list-collections.js +0 -0
  190. /package/{dist → lib}/utils/make-slug.js +0 -0
  191. /package/{dist → lib}/utils/module-dirname.js +0 -0
  192. /package/{dist → lib}/utils/natural-numbers.js +0 -0
  193. /package/{dist → lib}/utils/prettier.js +0 -0
  194. /package/{dist → lib}/utils/question.js +0 -0
  195. /package/{dist → lib}/utils/render-attributes.js +0 -0
  196. /package/{dist → lib}/utils/sleep.js +0 -0
  197. /package/{dist → lib}/utils/translate.js +0 -0
  198. /package/{dist → lib}/utils/url-with-new-params.js +0 -0
  199. /package/{dist → lib}/utils/utils.js +0 -0
  200. /package/{dist → lib}/utils/walk.js +0 -0
@@ -72,7 +72,7 @@ function multiform_factory(canAccessFun1, canAccessFun2) {
72
72
  }
73
73
  async function tests(js_enabled) {
74
74
  let page;
75
- beforeAll(async () => {
75
+ before(async () => {
76
76
  const browser = await getBrowser();
77
77
  const context = await browser.newContext({
78
78
  javaScriptEnabled: js_enabled
@@ -116,7 +116,7 @@ async function general_form_validation_e2e_tests(js_enabled) {
116
116
  async function individual_field_validation_e2e_tests(javaScriptEnabled) {
117
117
  let browser;
118
118
  let page;
119
- beforeAll(async () => {
119
+ before(async () => {
120
120
  browser = await getBrowser();
121
121
  const context = await browser.newContext({
122
122
  javaScriptEnabled
@@ -142,7 +142,7 @@ async function individual_field_validation_e2e_tests(javaScriptEnabled) {
142
142
  describe("multiform test", () => {
143
143
  describe("basic tests", async () => {
144
144
  let server;
145
- beforeAll(async () => {
145
+ before(async () => {
146
146
  const app = new Koa();
147
147
  const router = new Router();
148
148
  mount(
@@ -179,7 +179,7 @@ describe("multiform test", () => {
179
179
  app.use(router.routes()).use(router.allowedMethods());
180
180
  server = app.listen(port);
181
181
  });
182
- afterAll(() => {
182
+ after(() => {
183
183
  server.close();
184
184
  });
185
185
  describe("turbolinks + js enabled", async () => tests(true));
@@ -189,7 +189,7 @@ describe("multiform test", () => {
189
189
  let page;
190
190
  let server;
191
191
  describe("allow access to both forms when both canAccess return true", async () => {
192
- beforeAll(async () => {
192
+ before(async () => {
193
193
  const app = new Koa();
194
194
  const router = new Router();
195
195
  mount(
@@ -211,7 +211,7 @@ describe("multiform test", () => {
211
211
  const context = await browser.newContext();
212
212
  page = await context.newPage();
213
213
  });
214
- afterAll(async () => {
214
+ after(async () => {
215
215
  server.close();
216
216
  });
217
217
  it("check wether it renders", async () => {
@@ -241,7 +241,7 @@ describe("multiform test", () => {
241
241
  });
242
242
  });
243
243
  describe("deny access to both forms", async () => {
244
- beforeAll(async () => {
244
+ before(async () => {
245
245
  const app = new Koa();
246
246
  const router = new Router();
247
247
  mount(
@@ -263,7 +263,7 @@ describe("multiform test", () => {
263
263
  const context = await browser.newContext();
264
264
  page = await context.newPage();
265
265
  });
266
- afterAll(async () => {
266
+ after(async () => {
267
267
  server.close();
268
268
  });
269
269
  it("check wether forms didnt render", async () => {
@@ -289,7 +289,7 @@ describe("multiform test", () => {
289
289
  });
290
290
  });
291
291
  describe("allow access to one form, deny to the other", async () => {
292
- beforeAll(async () => {
292
+ before(async () => {
293
293
  const app = new Koa();
294
294
  const router = new Router();
295
295
  mount(
@@ -311,7 +311,7 @@ describe("multiform test", () => {
311
311
  const context = await browser.newContext({});
312
312
  page = await context.newPage();
313
313
  });
314
- afterAll(async () => {
314
+ after(async () => {
315
315
  server.close();
316
316
  });
317
317
  it("check wether form1 renders", async () => {
@@ -359,7 +359,7 @@ describe("multiform test", () => {
359
359
  describe("validation e2e", async () => {
360
360
  describe("basic tests", async () => {
361
361
  let server;
362
- beforeAll(async () => {
362
+ before(async () => {
363
363
  const app = new Koa();
364
364
  const router = new Router();
365
365
  mount(
@@ -417,7 +417,7 @@ describe("multiform test", () => {
417
417
  app.use(router.routes()).use(router.allowedMethods());
418
418
  server = app.listen(port);
419
419
  });
420
- afterAll(async () => {
420
+ after(async () => {
421
421
  server.close();
422
422
  });
423
423
  it("js enabled", async () => general_form_validation_e2e_tests(true));
@@ -425,7 +425,7 @@ describe("multiform test", () => {
425
425
  });
426
426
  describe("field specific validation", async () => {
427
427
  let server;
428
- beforeAll(async () => {
428
+ before(async () => {
429
429
  const app = new Koa();
430
430
  const router = new Router();
431
431
  const fields = {
@@ -478,7 +478,7 @@ describe("multiform test", () => {
478
478
  app.use(router.routes()).use(router.allowedMethods());
479
479
  server = app.listen(port);
480
480
  });
481
- afterAll(async () => {
481
+ after(async () => {
482
482
  server.close();
483
483
  });
484
484
  describe("js enabled", async () => individual_field_validation_e2e_tests(true));
@@ -14,7 +14,7 @@ async function generateComponents() {
14
14
  const component_data = component_names.map(
15
15
  (p) => "./" + path.relative(components_dir, p).replace(/\.[tj]sx?$/, ".js")
16
16
  ).map((relative_path) => {
17
- const name = relative_path.match(/\/([^\/]+).jdd/)?.[1];
17
+ const name = relative_path.match(/\/([^/]+).jdd/)?.[1];
18
18
  return { name, relative_path };
19
19
  });
20
20
  const content = `// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-components'
@@ -1,6 +1,7 @@
1
1
  import { generateURL } from "./generate-routes.js";
2
2
  import * as prettier from "prettier";
3
3
  import { getPrettierConfig } from "./utils/prettier.js";
4
+ import assert from "node:assert";
4
5
  describe("generate routes", () => {
5
6
  it("handles a url with params", async () => {
6
7
  assert.strictEqual(
@@ -13,7 +13,7 @@ async function generateStimulusControllers() {
13
13
  const controller_data = controller_names.map(
14
14
  (p) => "./" + path.relative(target_locreq.resolve(dirname(output_file)), p).replace(/\.[tj]sx?$/, ".js")
15
15
  ).map((relative_path) => {
16
- const name = relative_path.match(/\/([^\/]+).stimulus/)?.[1];
16
+ const name = relative_path.match(/\/([^/]+).stimulus/)?.[1];
17
17
  return { name, relative_path };
18
18
  });
19
19
  const content = `// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-stimulus'
@@ -1,3 +1,4 @@
1
+ import { FileManager } from "@sealcode/file-manager";
1
2
  import { Middlewares as SealiousMiddlewares } from "sealious";
2
3
  async function handleHtmlPromise(ctx, next) {
3
4
  await next();
@@ -6,7 +7,7 @@ async function handleHtmlPromise(ctx, next) {
6
7
  }
7
8
  ctx.set("content-type", "text/html;charset=utf-8");
8
9
  }
9
- function mount(router, url, mountable, use_dummy_app = false) {
10
+ function mount(router, url, mountable, use_dummy_app = false, file_manager = use_dummy_app ? new FileManager("/tmp", "/uploaded_files") : void 0) {
10
11
  const raw_url = typeof url === "string" ? url : url.rawURL;
11
12
  const args = use_dummy_app ? [
12
13
  async (ctx, next) => {
@@ -31,11 +32,11 @@ function mount(router, url, mountable, use_dummy_app = false) {
31
32
  };
32
33
  await next();
33
34
  },
34
- SealiousMiddlewares.parseBody(),
35
+ SealiousMiddlewares.parseBody(file_manager),
35
36
  handleHtmlPromise
36
37
  ] : [
37
38
  SealiousMiddlewares.extractContext(),
38
- SealiousMiddlewares.parseBody(),
39
+ SealiousMiddlewares.parseBody(file_manager),
39
40
  handleHtmlPromise
40
41
  ];
41
42
  router.use(raw_url, ...args);
@@ -10,7 +10,9 @@ function make_notifier(port) {
10
10
  sockets = sockets.filter((s) => s !== socket);
11
11
  });
12
12
  });
13
- console.log("build notifier listening on websocket at port " + port);
13
+ console.log(
14
+ "build notifier listening on websocket at port " + port.toString()
15
+ );
14
16
  return function notify(message) {
15
17
  sockets.forEach((s) => s.send(message));
16
18
  };
@@ -40,7 +40,7 @@ class ListPage extends Page {
40
40
  init() {
41
41
  super.init();
42
42
  for (const [fieldname, field] of Object.entries(this.filterFields)) {
43
- field.init(fieldname);
43
+ void field.init(fieldname);
44
44
  }
45
45
  }
46
46
  renderListContainer(_, content) {
@@ -14,7 +14,7 @@ class MountableWithFields extends Mountable {
14
14
  }
15
15
  init() {
16
16
  for (const [name, field] of Object.entries(this.fields)) {
17
- field.init(name);
17
+ void field.init(name);
18
18
  }
19
19
  }
20
20
  makeFormControlContext(ctx, data, validate, field_name_prefix = this.field_names_prefix, form_id = this.form_id) {
@@ -45,13 +45,13 @@ class SealiousItemListPage extends ListPage {
45
45
  async getFilterValues(ctx) {
46
46
  const query_params = qs.parse(ctx.search.slice(1));
47
47
  const _filter_values = query_params?.filter || {};
48
- let filter_values = is(
48
+ const filter_values = is(
49
49
  _filter_values,
50
50
  predicates.record(predicates.string, predicates.string)
51
51
  ) ? _filter_values : {};
52
52
  return filter_values;
53
53
  }
54
- async renderItem(_, item, index) {
54
+ async renderItem(_, item, _index) {
55
55
  return `<div>${item.id}</div>`;
56
56
  }
57
57
  }
@@ -3,6 +3,7 @@ import deepmerge from "deepmerge";
3
3
  import { tempstream } from "tempstream";
4
4
  import { from_base64, to_base64 } from "../utils/base64.js";
5
5
  import { Mountable } from "./mountable.js";
6
+ import { isPlainObject } from "is-what";
6
7
  class StatefulPage extends Mountable {
7
8
  async canAccess() {
8
9
  return { canAccess: true, message: "" };
@@ -13,7 +14,8 @@ class StatefulPage extends Mountable {
13
14
  this.render = async (ctx, state, inputs) => {
14
15
  return this.wrapInLayout(
15
16
  ctx,
16
- this.wrapInForm(
17
+ await this.wrapInForm(
18
+ ctx,
17
19
  state,
18
20
  await original_render(ctx, state, inputs)
19
21
  ),
@@ -21,12 +23,16 @@ class StatefulPage extends Mountable {
21
23
  );
22
24
  };
23
25
  }
24
- wrapInForm(state, content) {
25
- return tempstream`<form action="./" method="POST">
26
+ async wrapInForm(context, state, content) {
27
+ return tempstream`<form
28
+ action="./"
29
+ method="POST"
30
+ enctype="multipart/form-data"
31
+ >
26
32
  <input
27
33
  name="state"
28
34
  type="hidden"
29
- value="${to_base64(JSON.stringify(state))}"
35
+ value="${to_base64(await this.serializeState(context, state))}"
30
36
  />
31
37
  ${content}
32
38
  </form>`;
@@ -52,7 +58,7 @@ class StatefulPage extends Mountable {
52
58
  )}`;
53
59
  }
54
60
  makeActionButton(_state, action_description, ...args) {
55
- let { label, content } = this.extractActionAndLabel(action_description);
61
+ const { label, content } = this.extractActionAndLabel(action_description);
56
62
  return (
57
63
  /* HTML */
58
64
  `
@@ -78,10 +84,20 @@ class StatefulPage extends Mountable {
78
84
  async preprocessState(values) {
79
85
  return values;
80
86
  }
81
- async preprocessOverrides(state, values) {
87
+ async preprocessOverrides(_state, values) {
82
88
  return values;
83
89
  }
90
+ async serializeState(_context, state) {
91
+ return JSON.stringify(state);
92
+ }
93
+ async deserializeState(_context, s) {
94
+ const deserialized = JSON.parse(s);
95
+ console.log("stateful-page.ts:183", "deserialized:");
96
+ console.dir(deserialized, { depth: 10 });
97
+ return deserialized;
98
+ }
84
99
  async extractState(ctx) {
100
+ console.log("stateful-page.ts:188", "EXTRACT STATE");
85
101
  if (!hasShape(
86
102
  {
87
103
  action: predicates.maybe(predicates.string),
@@ -100,7 +116,8 @@ class StatefulPage extends Mountable {
100
116
  )
101
117
  );
102
118
  const original_state_string = ctx.$body.state;
103
- const original_state = JSON.parse(
119
+ const original_state = await this.deserializeState(
120
+ ctx,
104
121
  typeof original_state_string == "string" ? from_base64(original_state_string) : "{}"
105
122
  );
106
123
  const $body = ctx.$body;
@@ -110,6 +127,7 @@ class StatefulPage extends Mountable {
110
127
  state_overrides
111
128
  );
112
129
  let modified_state = deepmerge(original_state, state_overrides, {
130
+ isMergeableObject: isPlainObject,
113
131
  arrayMerge: (target, source, options) => {
114
132
  const destination = target.slice();
115
133
  source.forEach((item, index) => {
@@ -156,17 +174,16 @@ class StatefulPage extends Mountable {
156
174
  router.post(path, async (ctx) => {
157
175
  const { action, state, inputs, action_args } = await this.extractState(ctx);
158
176
  if (action) {
159
- ctx.body = this.render(
160
- ctx,
161
- await this.actions[action](
162
- state,
163
- inputs,
164
- ...JSON.parse(
165
- from_base64(action_args)
166
- )
167
- ),
168
- inputs
177
+ const new_state = await this.actions[action](
178
+ state,
179
+ inputs,
180
+ ...JSON.parse(
181
+ from_base64(action_args)
182
+ )
169
183
  );
184
+ console.log("stateful-page.ts:291", "new_state:");
185
+ console.dir(new_state, { depth: 10 });
186
+ ctx.body = this.render(ctx, new_state, inputs);
170
187
  } else {
171
188
  ctx.body = this.render(ctx, state, inputs);
172
189
  }
@@ -3,7 +3,7 @@ function jddComponentTemplate(component_name) {
3
3
  return `import type { FlatTemplatable } from "tempstream";
4
4
  import { TempstreamJSX } from "tempstream";
5
5
  import type {
6
- ExtractStructuredComponentArgumentsValues,
6
+ ExtractStructuredComponentArgumentsParsed,
7
7
  JDDContext,
8
8
  } from "@sealcode/jdd";
9
9
  import { Component, ComponentArguments } from "@sealcode/jdd";
@@ -24,7 +24,7 @@ export class ${toPascalCase(
24
24
  {
25
25
  title,
26
26
  content,
27
- }: ExtractStructuredComponentArgumentsValues<typeof component_arguments>,
27
+ }: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
28
28
  { render_markdown }: JDDContext
29
29
  ): FlatTemplatable {
30
30
  return (
@@ -2,6 +2,7 @@ import _locreq from "locreq";
2
2
  import { exec } from "./utils.js";
3
3
  const target_locreq = _locreq(process.cwd());
4
4
  import { promises as fs } from "fs";
5
+ import { is, predicates } from "@sealcode/ts-predicates";
5
6
  async function extract_fields_from_collection(collection_name) {
6
7
  const extractor_code = `import {default as the_app} from "./app.js";
7
8
  const c = new the_app().collections["${collection_name}"];
@@ -22,7 +23,21 @@ console.log(JSON.stringify(fields));
22
23
  await fs.writeFile(extractor_code_path, extractor_code);
23
24
  const { stdout } = await exec("node", [extractor_code_path]);
24
25
  await fs.unlink(extractor_code_path);
25
- return JSON.parse(stdout);
26
+ const ret = JSON.parse(stdout);
27
+ if (!is(
28
+ ret,
29
+ predicates.array(
30
+ predicates.shape({
31
+ name: predicates.string,
32
+ type: predicates.string
33
+ })
34
+ )
35
+ )) {
36
+ throw new Error(
37
+ "Encountered a problem while extracting the names of fields from collection. Got: " + stdout
38
+ );
39
+ }
40
+ return ret;
26
41
  }
27
42
  export {
28
43
  extract_fields_from_collection as default
package/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "@sealcode/sealgen",
3
- "version": "0.15.2",
3
+ "version": "0.15.4",
4
4
  "description": "Module to automate adding routes and collections to a sealious application",
5
- "main": "dist/index.js",
5
+ "main": "lib/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
+ "prebuild": "npm run lint && rm -rf lib",
8
9
  "build": "node ./build.js",
10
+ "pretest": "npm run build",
11
+ "test": "node test.cjs",
9
12
  "typecheck": "tsc --noemit",
10
- "prepare": "rm -rf @types && npm run typecheck && tsc --emitDeclarationOnly && npm run build",
11
- "watch": "npm run build -- --watch",
12
- "test-cmd": "vitest --config ./src/vitest.config.ts",
13
- "test-cmd-once": "vitest run --config ./src/vitest.config.ts",
14
- "test": "npm run test-cmd -- --ui",
15
- "coverage": "npm run test --coverage",
16
- "test-reports": "rm -f .xunit && npm run test-cmd-once -- --coverage --reporter=junit --outputFile=.xunit",
17
- "show-coverage": "npm run test-reports; xdg-open coverage/index.html"
13
+ "prepare": "npm run build",
14
+ "lint": "eslint src",
15
+ "preinstrument": "npm run build && rm -fr .xunit coverage lib-instrumented",
16
+ "instrument": "npx nyc instrument --exclude \"\" lib lib-instrumented",
17
+ "pretest-reports": "npm run instrument",
18
+ "test-reports": "npx nyc --exclude \"\" ./node_modules/.bin/mocha --recursive --timeout=10000 --require source-map-support/register --reporter xunit --reporter-option output=.xunit 'lib-instrumented/**/*.test.js' --exit && nyc report --reporter clover --exclude \"\"",
19
+ "precover-html": "rm -rf coverate/lcov-report",
20
+ "cover-html": "npm run test-reports && nyc report --reporter lcov --exclude \"\" && xdg-open coverage/lcov-report/index.html"
18
21
  },
19
22
  "bin": {
20
23
  "sealgen": "./dist/cli.js"
@@ -25,15 +28,15 @@
25
28
  "@playwright/test": "^1.30.0",
26
29
  "@types/koa__router": "^12.0.4",
27
30
  "@types/md5": "^2.3.5",
31
+ "@types/mocha": "^10.0.6",
28
32
  "@types/node": "^20.8.4",
29
33
  "@types/prettier": "^2.7.0",
30
34
  "@types/prompts": "^2.0.14",
31
35
  "@types/recursive-readdir": "^2.2.4",
32
36
  "@types/ws": "^8.5.10",
33
37
  "@types/yargs": "^17.0.32",
34
- "@vitest/coverage-istanbul": "^1.1.0",
35
- "@vitest/coverage-v8": "^1.1.0",
36
- "@vitest/ui": "^1.1.0",
38
+ "@typescript-eslint/eslint-plugin": "^5.58.0",
39
+ "@typescript-eslint/parser": "^5.58.0",
37
40
  "axios": "^1.6.7",
38
41
  "eslint": "^7.32.0",
39
42
  "eslint-config-prettier": "^8.3.0",
@@ -41,12 +44,13 @@
41
44
  "eslint-plugin-with-tsc-error": "^0.0.7",
42
45
  "get-port": "^5.1.1",
43
46
  "koa-body": "^5.0.0",
47
+ "mocha": "^10.4.0",
48
+ "mri": "^1.2.0",
44
49
  "nyc": "^15.1.0",
45
50
  "playwright": "^1.29.2",
46
51
  "source-map-support": "^0.5.21",
47
52
  "ts-node": "^10.9.1",
48
53
  "typescript": "^4.7",
49
- "vitest": "^1.1.0",
50
54
  "wait-port": "^1.0.4"
51
55
  },
52
56
  "types": "./@types/src/index.d.ts",
@@ -58,6 +62,7 @@
58
62
  "esbuild": "^0.20.0",
59
63
  "escape-goat": "^4.0.0",
60
64
  "google-fonts-helper": "^3.4.1",
65
+ "is-what": "^4.1.16",
61
66
  "js-convert-case": "^4.2.0",
62
67
  "locreq": "^3.0.0",
63
68
  "md5": "^2.3.0",
@@ -73,6 +78,6 @@
73
78
  "peerDependencies": {
74
79
  "koa": "^2.13.0",
75
80
  "koa-responsive-image-router": "^0.2.24",
76
- "sealious": "^0.19.6"
81
+ "sealious": "^0.19.7"
77
82
  }
78
83
  }
@@ -54,7 +54,7 @@ describe("add route test", () => {
54
54
  it("creates minimal sealious app", async function () {
55
55
  console.log("\tcloning sealious-minimal...");
56
56
  await execAsync(
57
- `git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout 710b873`
57
+ `git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout 5b33aac`
58
58
  );
59
59
  console.log("\tinstalling dependencies and sealgen...");
60
60
  await execAsync(
@@ -102,5 +102,5 @@ describe("add route test", () => {
102
102
  if (res.status != 200) {
103
103
  throw new Error("url generated by add-route exists but doesnt return 200");
104
104
  }
105
- }, 250000);
105
+ });
106
106
  });
package/src/add-route.ts CHANGED
@@ -21,7 +21,7 @@ export async function addRoute(
21
21
  message: 'What\'s the name of the action (e.g. "AddUser"): ',
22
22
  validate: (s: string) =>
23
23
  s.length > 3 ? true : "Should be at least 3 characters long",
24
- format: function (action) {
24
+ format: function (action: string) {
25
25
  return toPascalCase(action);
26
26
  },
27
27
  },
@@ -32,7 +32,7 @@ export async function addRoute(
32
32
  "Enter a full absolute path for the new route (for example: /admin/users/:id/edit): ",
33
33
  validate: (s: string) =>
34
34
  s.trim()[0] == "/" ? true : "Should start with a '/'",
35
- initial: function (_, { action }) {
35
+ initial: function (_, { action }: { action: string }) {
36
36
  return "/" + toKebabCase(action);
37
37
  },
38
38
  },
package/src/build.ts CHANGED
@@ -45,19 +45,19 @@ async function build(watch: boolean): Promise<void> {
45
45
  const port = await get_notifier_port();
46
46
  const notifier = make_notifier(port);
47
47
  await write_notifier_config(watch, port);
48
- watcher.on("all", async (_, file_path) => {
48
+ watcher.on("all", (_, file_path) => {
49
49
  console.log("Detected a change!", file_path);
50
50
  builders.forEach((builder) => {
51
51
  const owns_a_file = builder.ownsFile(file_path);
52
52
  if (owns_a_file) {
53
- builder.build(notifier);
53
+ void builder.build(notifier);
54
54
  }
55
55
  });
56
56
  });
57
57
  } else {
58
58
  await write_notifier_config(watch);
59
59
  builders.forEach((builder) => {
60
- builder.dispose();
60
+ void builder.dispose();
61
61
  });
62
62
  }
63
63
  }
package/src/builder.ts CHANGED
@@ -16,8 +16,6 @@ export abstract class Builder {
16
16
  abstract _build(): Promise<void>;
17
17
  abstract dispose(): Promise<void>;
18
18
 
19
- constructor() {}
20
-
21
19
  public ongoing_build: Promise<void> | null = null;
22
20
 
23
21
  public build(notifier?: (message: string) => void) {
@@ -92,6 +90,7 @@ export class FontsBuilder extends Builder {
92
90
  return file_path == FONTS_CONFIG_PATH;
93
91
  }
94
92
 
93
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
95
94
  async dispose(): Promise<void> {}
96
95
 
97
96
  async _build() {
@@ -115,6 +114,7 @@ export class BackendTSBuilder extends Builder {
115
114
  );
116
115
  }
117
116
 
117
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
118
118
  async dispose(): Promise<void> {}
119
119
 
120
120
  async _build(): Promise<void> {
@@ -160,6 +160,7 @@ export class FrontendTSBuilder extends Builder {
160
160
  );
161
161
  }
162
162
 
163
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
163
164
  async dispose(): Promise<void> {}
164
165
 
165
166
  async _build(): Promise<void> {
@@ -5,6 +5,7 @@ import path, { basename } from "node:path";
5
5
 
6
6
  import _locreq from "locreq";
7
7
  import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
8
+ import { PluginBuild } from "esbuild";
8
9
  const target_locreq = _locreq(process.cwd());
9
10
 
10
11
  function addSuffix(filename: string, suffix: string) {
@@ -12,7 +13,7 @@ function addSuffix(filename: string, suffix: string) {
12
13
  }
13
14
 
14
15
  async function makeResourceURL(file_path: string) {
15
- const hash_base = file_path + (await fs.stat(file_path)).mtimeMs;
16
+ const hash_base = file_path + (await fs.stat(file_path)).mtimeMs.toString();
16
17
  const hash = md5(hash_base);
17
18
  const public_url =
18
19
  "dist/" + addSuffix(basename(file_path), "-" + hash.slice(0, 8));
@@ -26,15 +27,15 @@ async function makeResourceURL(file_path: string) {
26
27
 
27
28
  export const load_assets_plugin = {
28
29
  name: "sealgen-load-assets",
29
- setup(build: any) {
30
+ setup(build: PluginBuild) {
30
31
  build.onLoad(
31
32
  {
32
33
  filter: new RegExp(
33
34
  `\\.(${embeddable_file_extensions.join("|")})$`
34
35
  ),
35
36
  },
36
- async (args: any) => {
37
- let contents = `export const url = "${await makeResourceURL(
37
+ async (args) => {
38
+ const contents = `export const url = "${await makeResourceURL(
38
39
  args.path
39
40
  )}";
40
41
 
@@ -2,11 +2,12 @@ import { promises as fs } from "fs";
2
2
  import { extname } from "node:path";
3
3
  import { embeddable_file_extensions } from "../embeddable-file-extensions.js";
4
4
  import { removeNamedImports } from "../../remove-named-imports.js";
5
+ import { PluginBuild } from "esbuild";
5
6
 
6
7
  export const rewrite_asset_imports_plugin = {
7
8
  name: "sealgen-rewrite-asset-imports",
8
- setup(build: any) {
9
- build.onLoad({ filter: /\.tsx?$/ }, async (args: any) => {
9
+ setup(build: PluginBuild) {
10
+ build.onLoad({ filter: /\.tsx?$/ }, async (args) => {
10
11
  let contents = await fs.readFile(args.path, "utf8");
11
12
  for (const ext of embeddable_file_extensions) {
12
13
  const regex_assets = new RegExp(
@@ -18,7 +19,7 @@ export const rewrite_asset_imports_plugin = {
18
19
  });
19
20
  }
20
21
 
21
- contents = removeNamedImports(contents);
22
+ contents = removeNamedImports(contents) as string;
22
23
 
23
24
  return {
24
25
  contents,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import Router from "@koa/router";
2
3
  import { hasShape, predicates } from "@sealcode/ts-predicates";
3
4
  import { Context } from "koa";
@@ -57,7 +58,6 @@ export class StructuredArray<
57
58
  }
58
59
 
59
60
  makeSubfield(_field_name: string): FormField {
60
- const fields = this;
61
61
  return new SimpleFormField(false);
62
62
  }
63
63
 
@@ -77,6 +77,7 @@ export class StructuredArray<
77
77
  validate: false,
78
78
  };
79
79
  if (Array.isArray(control)) {
80
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
80
81
  const [constructor, field_name, params] = control;
81
82
  const field = this.makeSubfield(field_name);
82
83
  if (!field) {
@@ -173,13 +174,14 @@ export class StructuredArray<
173
174
  }
174
175
 
175
176
  async getTargetItemID(ctx: Context): Promise<string> {
176
- return ctx.params.id;
177
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
178
+ return ctx.params.id as string;
177
179
  }
178
180
 
179
181
  mount(router: Router) {
180
182
  console.log("mounting!", this.getActionURL());
181
183
  router.post(this.getActionURL(), async (ctx) => {
182
- let form_id = ctx.query.form_id;
184
+ const form_id = ctx.query.form_id;
183
185
  if (typeof form_id != "string") {
184
186
  throw new Error("Missing form_id query param");
185
187
  }