@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.
- package/.eslintrc.cjs +38 -0
- package/.xunit +53 -0
- package/@types/src/page/stateful-page.d.ts +4 -2
- package/build.js +1 -1
- package/{dist → lib}/add-route.test.js +2 -2
- package/{dist → lib}/build.js +3 -3
- package/{dist → lib}/builder.js +3 -0
- package/lib/cli.js +39 -0
- package/{dist → lib}/esbuild-plugins/load-assets.js +2 -2
- package/{dist → lib}/forms/controls/array.js +1 -2
- package/{dist → lib}/forms/controls/autocomplete.js +8 -10
- package/{dist → lib}/forms/controls/checkboxed-list-input.test.js +3 -2
- package/{dist → lib}/forms/controls/collapsible-field-group.js +2 -1
- package/{dist → lib}/forms/controls/editable-collection-subset.js +6 -4
- package/{dist → lib}/forms/controls/field-group.js +1 -1
- package/{dist → lib}/forms/controls/file.js +2 -5
- package/{dist → lib}/forms/controls/form-control.js +2 -1
- package/{dist → lib}/forms/controls/photo.js +1 -1
- package/{dist → lib}/forms/controls/photo.test.js +5 -7
- package/{dist → lib}/forms/controls/textarea.js +1 -1
- package/{dist → lib}/forms/fields/boolean.test.js +0 -1
- package/{dist → lib}/forms/fields/field.js +2 -5
- package/{dist → lib}/forms/fields/file.js +8 -2
- package/{dist → lib}/forms/fields/structured-array.js +1 -1
- package/{dist → lib}/forms/form.test.js +8 -9
- package/{dist → lib}/forms/multiform.test.js +14 -14
- package/{dist → lib}/generate-components.js +1 -1
- package/{dist → lib}/generate-routes.test.js +1 -0
- package/{dist → lib}/generate-stimulus.js +1 -1
- package/{dist → lib}/mount.js +4 -3
- package/{dist → lib}/notifier.js +3 -1
- package/{dist → lib}/page/list.js +1 -1
- package/{dist → lib}/page/mountable-with-fields.js +1 -1
- package/{dist → lib}/page/sealious-list.js +2 -2
- package/{dist → lib}/page/stateful-page.js +34 -17
- package/{dist → lib}/templates/jdd-component.js +2 -2
- package/{dist → lib}/utils/extract-fields-from-collection.js +16 -1
- package/package.json +20 -15
- package/src/add-route.test.ts +2 -2
- package/src/add-route.ts +2 -2
- package/src/build.ts +3 -3
- package/src/builder.ts +3 -2
- package/src/esbuild-plugins/load-assets.ts +5 -4
- package/src/esbuild-plugins/rewrite-asset-imports.ts +4 -3
- package/src/forms/controls/array.ts +5 -3
- package/src/forms/controls/autocomplete.ts +2 -2
- package/src/forms/controls/checkbox-group.ts +1 -5
- package/src/forms/controls/checkboxed-list-input.test.ts +3 -2
- package/src/forms/controls/checkboxed-list-input.ts +0 -1
- package/src/forms/controls/collapsible-field-group.ts +4 -2
- package/src/forms/controls/derived.ts +1 -1
- package/src/forms/controls/dropdown.ts +3 -3
- package/src/forms/controls/editable-collection-subset.ts +10 -4
- package/src/forms/controls/field-group.ts +1 -1
- package/src/forms/controls/file.ts +1 -1
- package/src/forms/controls/form-control.ts +2 -1
- package/src/forms/controls/hidden-input.ts +0 -2
- package/src/forms/controls/multiple-files.ts +8 -5
- package/src/forms/controls/number.ts +1 -1
- package/src/forms/controls/photo.test.ts +6 -7
- package/src/forms/controls/photo.ts +3 -6
- package/src/forms/controls/simple-input.ts +3 -5
- package/src/forms/controls/textarea.ts +3 -8
- package/src/forms/controls/tickable.ts +2 -4
- package/src/forms/fields/boolean.test.ts +0 -1
- package/src/forms/fields/existing-sealious-value.ts +1 -0
- package/src/forms/fields/field.ts +5 -8
- package/src/forms/fields/file.ts +13 -6
- package/src/forms/fields/structured-array.ts +2 -2
- package/src/forms/form.test.ts +9 -10
- package/src/forms/form.ts +1 -1
- package/src/forms/multiform.test.ts +14 -14
- package/src/forms/multiform.ts +2 -1
- package/src/generate-components.ts +1 -2
- package/src/generate-routes.test.ts +1 -0
- package/src/generate-stimulus.ts +1 -1
- package/src/get-fonts.ts +2 -0
- package/src/mount.ts +8 -3
- package/src/notifier.ts +3 -1
- package/src/page/list.ts +1 -1
- package/src/page/mountable-with-fields.ts +3 -1
- package/src/page/sealious-list.ts +3 -2
- package/src/page/stateful-page.ts +50 -22
- package/src/templates/jdd-component.ts +2 -2
- package/src/utils/extract-fields-from-collection.ts +19 -1
- package/src/utils/translate.ts +1 -1
- package/src/utils/utils.ts +2 -0
- package/test.cjs +54 -0
- package/tsconfig.json +3 -4
- package/dist/back/routes/sample.page.js +0 -17
- package/dist/back/routes/sample.test.js +0 -39
- package/dist/hehe.test.js +0 -7
- package/dist/sample-index.js +0 -3
- package/dist/sample-lib.js +0 -9
- package/dist/vitest.config.js +0 -23
- package/src/vitest.config.ts +0 -21
- /package/{dist → lib}/add-collection.js +0 -0
- /package/{dist → lib}/add-jdd-component.js +0 -0
- /package/{dist → lib}/add-route.js +0 -0
- /package/{dist → lib}/components/components.js +0 -0
- /package/{dist → lib}/components/tabs.js +0 -0
- /package/{dist → lib}/components/toggle-modal.js +0 -0
- /package/{dist → lib}/embeddable-file-extensions.js +0 -0
- /package/{dist → lib}/esbuild-plugins/rewrite-asset-imports.js +0 -0
- /package/{dist → lib}/example.test.js +0 -0
- /package/{dist → lib}/find-css-includes.js +0 -0
- /package/{dist → lib}/forms/controls/big-radio-group.js +0 -0
- /package/{dist → lib}/forms/controls/checkbox-group.js +0 -0
- /package/{dist → lib}/forms/controls/checkbox.js +0 -0
- /package/{dist → lib}/forms/controls/checkboxed-list-input.js +0 -0
- /package/{dist → lib}/forms/controls/collapsible-checkbox-group.js +0 -0
- /package/{dist → lib}/forms/controls/controls.js +0 -0
- /package/{dist → lib}/forms/controls/derived-frame.js +0 -0
- /package/{dist → lib}/forms/controls/derived.js +0 -0
- /package/{dist → lib}/forms/controls/dropdown.js +0 -0
- /package/{dist → lib}/forms/controls/form-field-control.js +0 -0
- /package/{dist → lib}/forms/controls/form-header.js +0 -0
- /package/{dist → lib}/forms/controls/form-paragraph.js +0 -0
- /package/{dist → lib}/forms/controls/frame.js +0 -0
- /package/{dist → lib}/forms/controls/hidden-input.js +0 -0
- /package/{dist → lib}/forms/controls/html.js +0 -0
- /package/{dist → lib}/forms/controls/multiple-files.js +0 -0
- /package/{dist → lib}/forms/controls/multiple-photos.js +0 -0
- /package/{dist → lib}/forms/controls/number.js +0 -0
- /package/{dist → lib}/forms/controls/radio.js +0 -0
- /package/{dist → lib}/forms/controls/reverse-single-reference.js +0 -0
- /package/{dist → lib}/forms/controls/simple-input.js +0 -0
- /package/{dist → lib}/forms/controls/tickable.js +0 -0
- /package/{dist → lib}/forms/fields/boolean.js +0 -0
- /package/{dist → lib}/forms/fields/checkboxed-list.js +0 -0
- /package/{dist → lib}/forms/fields/collection-field.js +0 -0
- /package/{dist → lib}/forms/fields/email.js +0 -0
- /package/{dist → lib}/forms/fields/email.test.js +0 -0
- /package/{dist → lib}/forms/fields/enum-multiple.js +0 -0
- /package/{dist → lib}/forms/fields/enum-multiple.test.js +0 -0
- /package/{dist → lib}/forms/fields/enum.js +0 -0
- /package/{dist → lib}/forms/fields/enum.test.js +0 -0
- /package/{dist → lib}/forms/fields/existing-sealious-value.js +0 -0
- /package/{dist → lib}/forms/fields/fields.js +0 -0
- /package/{dist → lib}/forms/fields/multiple-files.js +0 -0
- /package/{dist → lib}/forms/fields/number.js +0 -0
- /package/{dist → lib}/forms/fields/number.test.js +0 -0
- /package/{dist → lib}/forms/fields/pick-from-list.js +0 -0
- /package/{dist → lib}/forms/fields/pick-from-list.test.js +0 -0
- /package/{dist → lib}/forms/fields/proxy-field.js +0 -0
- /package/{dist → lib}/forms/fields/regexp.js +0 -0
- /package/{dist → lib}/forms/fields/regexp.test.js +0 -0
- /package/{dist → lib}/forms/fields/reverse-single-reference.js +0 -0
- /package/{dist → lib}/forms/fields/simple-form-field.js +0 -0
- /package/{dist → lib}/forms/form-types.js +0 -0
- /package/{dist → lib}/forms/form.js +0 -0
- /package/{dist → lib}/forms/multiform.js +0 -0
- /package/{dist → lib}/generate-collections.js +0 -0
- /package/{dist → lib}/generate-routes.js +0 -0
- /package/{dist → lib}/get-fonts.js +0 -0
- /package/{dist → lib}/index.js +0 -0
- /package/{dist → lib}/koa.d.js +0 -0
- /package/{dist → lib}/make-env.js +0 -0
- /package/{dist → lib}/make-hidden-inputs.js +0 -0
- /package/{dist → lib}/page/access-control.js +0 -0
- /package/{dist → lib}/page/default-list-filters.js +0 -0
- /package/{dist → lib}/page/mountable.js +0 -0
- /package/{dist → lib}/page/page.js +0 -0
- /package/{dist → lib}/page/props-parser.js +0 -0
- /package/{dist → lib}/run.js +0 -0
- /package/{dist → lib}/sanitize.js +0 -0
- /package/{dist → lib}/target-locreq.js +0 -0
- /package/{dist → lib}/templates/collection.js +0 -0
- /package/{dist → lib}/templates/form.js +0 -0
- /package/{dist → lib}/templates/list.js +0 -0
- /package/{dist → lib}/templates/long-running-process.js +0 -0
- /package/{dist → lib}/templates/multiform.js +0 -0
- /package/{dist → lib}/templates/page.js +0 -0
- /package/{dist → lib}/templates/post.js +0 -0
- /package/{dist → lib}/templates/redirect.js +0 -0
- /package/{dist → lib}/templates/route-test.js +0 -0
- /package/{dist → lib}/templates/stateful-page.js +0 -0
- /package/{dist → lib}/templates/stimulus-controller.js +0 -0
- /package/{dist → lib}/templates/templates.js +0 -0
- /package/{dist → lib}/test_utils/locator_is_visible.js +0 -0
- /package/{dist → lib}/utils/assert-throws.js +0 -0
- /package/{dist → lib}/utils/base64.js +0 -0
- /package/{dist → lib}/utils/browser-creator.js +0 -0
- /package/{dist → lib}/utils/escape-url-params.js +0 -0
- /package/{dist → lib}/utils/exec.js +0 -0
- /package/{dist → lib}/utils/format-external-url.js +0 -0
- /package/{dist → lib}/utils/import-path.js +0 -0
- /package/{dist → lib}/utils/input-wrapper.js +0 -0
- /package/{dist → lib}/utils/list-collections.js +0 -0
- /package/{dist → lib}/utils/make-slug.js +0 -0
- /package/{dist → lib}/utils/module-dirname.js +0 -0
- /package/{dist → lib}/utils/natural-numbers.js +0 -0
- /package/{dist → lib}/utils/prettier.js +0 -0
- /package/{dist → lib}/utils/question.js +0 -0
- /package/{dist → lib}/utils/render-attributes.js +0 -0
- /package/{dist → lib}/utils/sleep.js +0 -0
- /package/{dist → lib}/utils/translate.js +0 -0
- /package/{dist → lib}/utils/url-with-new-params.js +0 -0
- /package/{dist → lib}/utils/utils.js +0 -0
- /package/{dist → lib}/utils/walk.js +0 -0
|
@@ -77,7 +77,7 @@ function multiform_factory(
|
|
|
77
77
|
async function tests(js_enabled: boolean) {
|
|
78
78
|
let page: Page;
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
before(async () => {
|
|
81
81
|
const browser = await getBrowser();
|
|
82
82
|
const context = await browser.newContext({
|
|
83
83
|
javaScriptEnabled: js_enabled,
|
|
@@ -127,7 +127,7 @@ async function individual_field_validation_e2e_tests(javaScriptEnabled: boolean)
|
|
|
127
127
|
let browser: Browser;
|
|
128
128
|
let page: Page;
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
before(async () => {
|
|
131
131
|
browser = await getBrowser();
|
|
132
132
|
const context = await browser.newContext({
|
|
133
133
|
javaScriptEnabled,
|
|
@@ -157,7 +157,7 @@ describe("multiform test", () => {
|
|
|
157
157
|
describe("basic tests", async () => {
|
|
158
158
|
let server: ReturnType<Koa["listen"]>;
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
before(async () => {
|
|
161
161
|
const app = new Koa();
|
|
162
162
|
const router = new Router();
|
|
163
163
|
|
|
@@ -198,7 +198,7 @@ describe("multiform test", () => {
|
|
|
198
198
|
server = app.listen(port);
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
after(() => {
|
|
202
202
|
server.close();
|
|
203
203
|
});
|
|
204
204
|
|
|
@@ -211,7 +211,7 @@ describe("multiform test", () => {
|
|
|
211
211
|
let server: ReturnType<Koa["listen"]>;
|
|
212
212
|
|
|
213
213
|
describe("allow access to both forms when both canAccess return true", async () => {
|
|
214
|
-
|
|
214
|
+
before(async () => {
|
|
215
215
|
const app = new Koa();
|
|
216
216
|
const router = new Router();
|
|
217
217
|
|
|
@@ -239,7 +239,7 @@ describe("multiform test", () => {
|
|
|
239
239
|
page = await context.newPage();
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
|
|
242
|
+
after(async () => {
|
|
243
243
|
server.close();
|
|
244
244
|
});
|
|
245
245
|
|
|
@@ -272,7 +272,7 @@ describe("multiform test", () => {
|
|
|
272
272
|
});
|
|
273
273
|
|
|
274
274
|
describe("deny access to both forms", async () => {
|
|
275
|
-
|
|
275
|
+
before(async () => {
|
|
276
276
|
const app = new Koa();
|
|
277
277
|
const router = new Router();
|
|
278
278
|
|
|
@@ -300,7 +300,7 @@ describe("multiform test", () => {
|
|
|
300
300
|
page = await context.newPage();
|
|
301
301
|
});
|
|
302
302
|
|
|
303
|
-
|
|
303
|
+
after(async () => {
|
|
304
304
|
server.close();
|
|
305
305
|
});
|
|
306
306
|
|
|
@@ -331,7 +331,7 @@ describe("multiform test", () => {
|
|
|
331
331
|
});
|
|
332
332
|
|
|
333
333
|
describe("allow access to one form, deny to the other", async () => {
|
|
334
|
-
|
|
334
|
+
before(async () => {
|
|
335
335
|
const app = new Koa();
|
|
336
336
|
const router = new Router();
|
|
337
337
|
|
|
@@ -359,7 +359,7 @@ describe("multiform test", () => {
|
|
|
359
359
|
page = await context.newPage();
|
|
360
360
|
});
|
|
361
361
|
|
|
362
|
-
|
|
362
|
+
after(async () => {
|
|
363
363
|
server.close();
|
|
364
364
|
});
|
|
365
365
|
|
|
@@ -414,7 +414,7 @@ describe("multiform test", () => {
|
|
|
414
414
|
describe("basic tests", async () => {
|
|
415
415
|
let server: ReturnType<Koa["listen"]>;
|
|
416
416
|
|
|
417
|
-
|
|
417
|
+
before(async () => {
|
|
418
418
|
const app = new Koa();
|
|
419
419
|
const router = new Router();
|
|
420
420
|
|
|
@@ -475,7 +475,7 @@ describe("multiform test", () => {
|
|
|
475
475
|
server = app.listen(port);
|
|
476
476
|
});
|
|
477
477
|
|
|
478
|
-
|
|
478
|
+
after(async () => {
|
|
479
479
|
server.close();
|
|
480
480
|
});
|
|
481
481
|
|
|
@@ -486,7 +486,7 @@ describe("multiform test", () => {
|
|
|
486
486
|
describe("field specific validation", async () => {
|
|
487
487
|
let server: ReturnType<Koa["listen"]>;
|
|
488
488
|
|
|
489
|
-
|
|
489
|
+
before(async () => {
|
|
490
490
|
const app = new Koa();
|
|
491
491
|
const router = new Router();
|
|
492
492
|
const fields = {
|
|
@@ -538,7 +538,7 @@ describe("multiform test", () => {
|
|
|
538
538
|
server = app.listen(port);
|
|
539
539
|
});
|
|
540
540
|
|
|
541
|
-
|
|
541
|
+
after(async () => {
|
|
542
542
|
server.close();
|
|
543
543
|
});
|
|
544
544
|
|
package/src/forms/multiform.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import Router from "@koa/router";
|
|
2
3
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
3
4
|
import { Context } from "koa";
|
|
4
5
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
5
6
|
import { Fields, MountableWithFields } from "../page/mountable-with-fields.js";
|
|
6
7
|
import { attribute } from "../sanitize.js";
|
|
7
|
-
import type { FormDataValue, FormMessage
|
|
8
|
+
import type { FormDataValue, FormMessage } from "./form-types.js";
|
|
8
9
|
import { Form } from "./form.js";
|
|
9
10
|
|
|
10
11
|
const FIELD_PREFIX_SEPARATOR = "___";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _locreq from "locreq";
|
|
2
2
|
import * as prettier from "prettier";
|
|
3
3
|
import { promises as fs } from "fs";
|
|
4
|
-
import { assertType, predicates } from "@sealcode/ts-predicates";
|
|
5
4
|
import { getPrettierConfig } from "./utils/prettier.js";
|
|
6
5
|
import recursive from "recursive-readdir";
|
|
7
6
|
import path from "path";
|
|
@@ -23,7 +22,7 @@ export async function generateComponents(): Promise<void> {
|
|
|
23
22
|
path.relative(components_dir, p).replace(/\.[tj]sx?$/, ".js")
|
|
24
23
|
)
|
|
25
24
|
.map((relative_path) => {
|
|
26
|
-
const name = relative_path.match(/\/([
|
|
25
|
+
const name = relative_path.match(/\/([^/]+).jdd/)?.[1];
|
|
27
26
|
return { name, relative_path };
|
|
28
27
|
});
|
|
29
28
|
|
|
@@ -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
|
|
|
5
6
|
describe("generate routes", () => {
|
|
6
7
|
it("handles a url with params", async () => {
|
package/src/generate-stimulus.ts
CHANGED
|
@@ -23,7 +23,7 @@ export async function generateStimulusControllers(): Promise<void> {
|
|
|
23
23
|
.replace(/\.[tj]sx?$/, ".js")
|
|
24
24
|
)
|
|
25
25
|
.map((relative_path) => {
|
|
26
|
-
const name = relative_path.match(/\/([
|
|
26
|
+
const name = relative_path.match(/\/([^/]+).stimulus/)?.[1];
|
|
27
27
|
return { name, relative_path };
|
|
28
28
|
});
|
|
29
29
|
|
package/src/get-fonts.ts
CHANGED
|
@@ -7,7 +7,9 @@ export const FONTS_CONFIG_PATH = "src/fonts.json";
|
|
|
7
7
|
export async function getFonts(_: Record<string, string | boolean> = {}) {
|
|
8
8
|
const fonts_config_path = target_locreq.resolve(FONTS_CONFIG_PATH);
|
|
9
9
|
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
10
11
|
const fonts_config = JSON.parse(await readFile(fonts_config_path, "utf8"));
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
11
13
|
const fonts_url = constructURL(fonts_config.googleFonts);
|
|
12
14
|
if (!fonts_url) {
|
|
13
15
|
throw new Error("Invalid font config");
|
package/src/mount.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import Router from "@koa/router";
|
|
3
|
+
import { FileManager } from "@sealcode/file-manager";
|
|
2
4
|
import Koa, { BaseContext, Context } from "koa";
|
|
3
5
|
import { Middlewares as SealiousMiddlewares } from "sealious";
|
|
4
6
|
import { Mountable } from "./page/mountable.js";
|
|
@@ -16,7 +18,10 @@ export function mount(
|
|
|
16
18
|
router: Router,
|
|
17
19
|
url: string | { rawURL: string },
|
|
18
20
|
mountable: Mountable,
|
|
19
|
-
use_dummy_app = false
|
|
21
|
+
use_dummy_app = false,
|
|
22
|
+
file_manager: FileManager | undefined = use_dummy_app
|
|
23
|
+
? new FileManager("/tmp", "/uploaded_files")
|
|
24
|
+
: undefined
|
|
20
25
|
): void {
|
|
21
26
|
const raw_url = typeof url === "string" ? url : url.rawURL;
|
|
22
27
|
const args = use_dummy_app
|
|
@@ -48,12 +53,12 @@ export function mount(
|
|
|
48
53
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
49
54
|
await next();
|
|
50
55
|
},
|
|
51
|
-
SealiousMiddlewares.parseBody(),
|
|
56
|
+
SealiousMiddlewares.parseBody(file_manager),
|
|
52
57
|
handleHtmlPromise,
|
|
53
58
|
]
|
|
54
59
|
: [
|
|
55
60
|
SealiousMiddlewares.extractContext(),
|
|
56
|
-
SealiousMiddlewares.parseBody(),
|
|
61
|
+
SealiousMiddlewares.parseBody(file_manager),
|
|
57
62
|
handleHtmlPromise,
|
|
58
63
|
];
|
|
59
64
|
|
package/src/notifier.ts
CHANGED
|
@@ -15,7 +15,9 @@ export function make_notifier(port: number) {
|
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
console.log(
|
|
18
|
+
console.log(
|
|
19
|
+
"build notifier listening on websocket at port " + port.toString()
|
|
20
|
+
);
|
|
19
21
|
return function notify(message: string) {
|
|
20
22
|
sockets.forEach((s) => s.send(message));
|
|
21
23
|
};
|
package/src/page/list.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { Mountable } from "./mountable.js";
|
|
|
15
15
|
|
|
16
16
|
export type PageErrorMessage = { type: "access" | "internal"; message: string };
|
|
17
17
|
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
19
|
export type Fields = Record<string, FormField<boolean, any>>;
|
|
19
20
|
|
|
20
21
|
export abstract class MountableWithFields<
|
|
@@ -32,7 +33,7 @@ export abstract class MountableWithFields<
|
|
|
32
33
|
|
|
33
34
|
init(): void {
|
|
34
35
|
for (const [name, field] of Object.entries(this.fields)) {
|
|
35
|
-
field.init(name);
|
|
36
|
+
void field.init(name);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -141,6 +142,7 @@ export abstract class MountableWithFields<
|
|
|
141
142
|
const result: Record<string, unknown> = {};
|
|
142
143
|
const promises = Object.entries(this.fields).map(
|
|
143
144
|
async ([key, field]) => {
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
144
146
|
const { parsed } = await field.getValue(ctx, raw_values);
|
|
145
147
|
result[key] = parsed;
|
|
146
148
|
}
|
|
@@ -48,6 +48,7 @@ export abstract class SealiousItemListPage<
|
|
|
48
48
|
const query = this.collection.list(await this.getContextForList(ctx));
|
|
49
49
|
const sort = await this.getSort(ctx);
|
|
50
50
|
if (sort) {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
|
51
52
|
query.sort({ [sort.field]: sort.order } as any);
|
|
52
53
|
}
|
|
53
54
|
return query;
|
|
@@ -69,7 +70,7 @@ export abstract class SealiousItemListPage<
|
|
|
69
70
|
async getFilterValues(ctx: Context) {
|
|
70
71
|
const query_params = qs.parse(ctx.search.slice(1));
|
|
71
72
|
const _filter_values = query_params?.filter || {};
|
|
72
|
-
|
|
73
|
+
const filter_values: Record<string, string | undefined> = is(
|
|
73
74
|
_filter_values,
|
|
74
75
|
predicates.record(predicates.string, predicates.string)
|
|
75
76
|
)
|
|
@@ -81,7 +82,7 @@ export abstract class SealiousItemListPage<
|
|
|
81
82
|
async renderItem(
|
|
82
83
|
_: Context,
|
|
83
84
|
item: CollectionItem<C>,
|
|
84
|
-
|
|
85
|
+
_index: number
|
|
85
86
|
): Promise<FlatTemplatable> {
|
|
86
87
|
return `<div>${item.id}</div>`;
|
|
87
88
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import Router from "@koa/router";
|
|
2
|
-
import { predicates, hasShape, is
|
|
3
|
+
import { predicates, hasShape, is } from "@sealcode/ts-predicates";
|
|
3
4
|
import deepmerge, { ArrayMergeOptions } from "deepmerge";
|
|
4
|
-
import { BaseContext } from "koa";
|
|
5
|
+
import { BaseContext, Context } from "koa";
|
|
5
6
|
import { Templatable, tempstream } from "tempstream";
|
|
6
7
|
import { from_base64, to_base64 } from "../utils/base64.js";
|
|
7
8
|
import { ExtractTail } from "../utils/utils.js";
|
|
8
9
|
import { Mountable } from "./mountable.js";
|
|
10
|
+
import { isPlainObject } from "is-what";
|
|
9
11
|
|
|
10
12
|
export type StatefulPageActionDescription<ActionName> =
|
|
11
13
|
| {
|
|
@@ -55,7 +57,7 @@ export abstract class StatefulPage<
|
|
|
55
57
|
|
|
56
58
|
constructor() {
|
|
57
59
|
super();
|
|
58
|
-
const original_render = this.render.bind(this);
|
|
60
|
+
const original_render = this.render.bind(this) as typeof this.render;
|
|
59
61
|
this.render = async (
|
|
60
62
|
ctx: BaseContext,
|
|
61
63
|
state: State,
|
|
@@ -63,7 +65,8 @@ export abstract class StatefulPage<
|
|
|
63
65
|
) => {
|
|
64
66
|
return this.wrapInLayout(
|
|
65
67
|
ctx,
|
|
66
|
-
this.wrapInForm(
|
|
68
|
+
await this.wrapInForm(
|
|
69
|
+
ctx as Context,
|
|
67
70
|
state,
|
|
68
71
|
await original_render(ctx, state, inputs)
|
|
69
72
|
),
|
|
@@ -78,12 +81,20 @@ export abstract class StatefulPage<
|
|
|
78
81
|
state: State
|
|
79
82
|
): Templatable;
|
|
80
83
|
|
|
81
|
-
wrapInForm(
|
|
82
|
-
|
|
84
|
+
async wrapInForm(
|
|
85
|
+
context: Context,
|
|
86
|
+
state: State,
|
|
87
|
+
content: Templatable
|
|
88
|
+
): Promise<Templatable> {
|
|
89
|
+
return tempstream/* HTML */ `<form
|
|
90
|
+
action="./"
|
|
91
|
+
method="POST"
|
|
92
|
+
enctype="multipart/form-data"
|
|
93
|
+
>
|
|
83
94
|
<input
|
|
84
95
|
name="state"
|
|
85
96
|
type="hidden"
|
|
86
|
-
value="${to_base64(
|
|
97
|
+
value="${to_base64(await this.serializeState(context, state))}"
|
|
87
98
|
/>
|
|
88
99
|
${content}
|
|
89
100
|
</form>`;
|
|
@@ -121,7 +132,8 @@ export abstract class StatefulPage<
|
|
|
121
132
|
action_description: StatefulPageActionDescription<ActionName>,
|
|
122
133
|
...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
123
134
|
) {
|
|
124
|
-
|
|
135
|
+
const { label, content } =
|
|
136
|
+
this.extractActionAndLabel(action_description);
|
|
125
137
|
return /* HTML */ `
|
|
126
138
|
<button
|
|
127
139
|
type="submit"
|
|
@@ -157,15 +169,27 @@ export abstract class StatefulPage<
|
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
async preprocessOverrides(
|
|
160
|
-
|
|
172
|
+
_state: State,
|
|
161
173
|
values: Record<string, unknown>
|
|
162
174
|
): Promise<Record<string, unknown>> {
|
|
163
175
|
return values;
|
|
164
176
|
}
|
|
165
177
|
|
|
178
|
+
async serializeState(_context: BaseContext, state: State): Promise<string> {
|
|
179
|
+
return JSON.stringify(state);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async deserializeState(_context: BaseContext, s: string): Promise<State> {
|
|
183
|
+
const deserialized = JSON.parse(s) as unknown;
|
|
184
|
+
console.log("stateful-page.ts:183", "deserialized:");
|
|
185
|
+
console.dir(deserialized, { depth: 10 });
|
|
186
|
+
return deserialized as State;
|
|
187
|
+
}
|
|
188
|
+
|
|
166
189
|
async extractState(
|
|
167
190
|
ctx: BaseContext
|
|
168
191
|
): Promise<StateAndMetadata<State, Actions>> {
|
|
192
|
+
console.log("stateful-page.ts:188", "EXTRACT STATE");
|
|
169
193
|
if (
|
|
170
194
|
!hasShape(
|
|
171
195
|
{
|
|
@@ -189,7 +213,8 @@ export abstract class StatefulPage<
|
|
|
189
213
|
// the "$" key is parsed as dot notation and overrides the state
|
|
190
214
|
|
|
191
215
|
const original_state_string = ctx.$body.state;
|
|
192
|
-
const original_state =
|
|
216
|
+
const original_state = await this.deserializeState(
|
|
217
|
+
ctx,
|
|
193
218
|
typeof original_state_string == "string"
|
|
194
219
|
? from_base64(original_state_string)
|
|
195
220
|
: "{}"
|
|
@@ -202,6 +227,7 @@ export abstract class StatefulPage<
|
|
|
202
227
|
state_overrides
|
|
203
228
|
);
|
|
204
229
|
let modified_state = deepmerge(original_state, state_overrides, {
|
|
230
|
+
isMergeableObject: isPlainObject,
|
|
205
231
|
arrayMerge: (
|
|
206
232
|
target: any[],
|
|
207
233
|
source: any[],
|
|
@@ -209,7 +235,7 @@ export abstract class StatefulPage<
|
|
|
209
235
|
) => {
|
|
210
236
|
// https://github.com/TehShrike/deepmerge?tab=readme-ov-file#arraymerge-example-combine-arrays
|
|
211
237
|
const destination = target.slice();
|
|
212
|
-
|
|
238
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
213
239
|
source.forEach((item, index) => {
|
|
214
240
|
if (typeof destination[index] === "undefined") {
|
|
215
241
|
destination[index] =
|
|
@@ -218,15 +244,18 @@ export abstract class StatefulPage<
|
|
|
218
244
|
options
|
|
219
245
|
);
|
|
220
246
|
} else if (options.isMergeableObject(item)) {
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
221
248
|
destination[index] = deepmerge(
|
|
222
249
|
target[index],
|
|
223
250
|
item,
|
|
224
251
|
options
|
|
225
252
|
);
|
|
226
253
|
} else if (target.indexOf(item) === -1) {
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
227
255
|
destination[index] = item;
|
|
228
256
|
}
|
|
229
257
|
});
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
230
259
|
return destination;
|
|
231
260
|
},
|
|
232
261
|
}) as State;
|
|
@@ -237,7 +266,7 @@ export abstract class StatefulPage<
|
|
|
237
266
|
if (ctx.$body.action && ctx.$body.action_args) {
|
|
238
267
|
return {
|
|
239
268
|
state: modified_state,
|
|
240
|
-
action: ctx.$body.action
|
|
269
|
+
action: ctx.$body.action,
|
|
241
270
|
inputs,
|
|
242
271
|
action_args: ctx.$body.action_args,
|
|
243
272
|
};
|
|
@@ -261,17 +290,16 @@ export abstract class StatefulPage<
|
|
|
261
290
|
const { action, state, inputs, action_args } =
|
|
262
291
|
await this.extractState(ctx);
|
|
263
292
|
if (action) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
from_base64(action_args as string)
|
|
271
|
-
) as unknown[])
|
|
272
|
-
),
|
|
273
|
-
inputs
|
|
293
|
+
const new_state = await this.actions[action](
|
|
294
|
+
state,
|
|
295
|
+
inputs,
|
|
296
|
+
...(JSON.parse(
|
|
297
|
+
from_base64(action_args as string)
|
|
298
|
+
) as unknown[])
|
|
274
299
|
);
|
|
300
|
+
console.log("stateful-page.ts:291", "new_state:");
|
|
301
|
+
console.dir(new_state, { depth: 10 });
|
|
302
|
+
ctx.body = this.render(ctx, new_state, inputs);
|
|
275
303
|
} else {
|
|
276
304
|
ctx.body = this.render(ctx, state, inputs);
|
|
277
305
|
}
|
|
@@ -4,7 +4,7 @@ export function jddComponentTemplate(component_name: string) {
|
|
|
4
4
|
return `import type { FlatTemplatable } from "tempstream";
|
|
5
5
|
import { TempstreamJSX } from "tempstream";
|
|
6
6
|
import type {
|
|
7
|
-
|
|
7
|
+
ExtractStructuredComponentArgumentsParsed,
|
|
8
8
|
JDDContext,
|
|
9
9
|
} from "@sealcode/jdd";
|
|
10
10
|
import { Component, ComponentArguments } from "@sealcode/jdd";
|
|
@@ -25,7 +25,7 @@ export class ${toPascalCase(
|
|
|
25
25
|
{
|
|
26
26
|
title,
|
|
27
27
|
content,
|
|
28
|
-
}:
|
|
28
|
+
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
|
29
29
|
{ render_markdown }: JDDContext
|
|
30
30
|
): FlatTemplatable {
|
|
31
31
|
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
|
|
|
6
7
|
export default async function extract_fields_from_collection(
|
|
7
8
|
collection_name: string
|
|
@@ -25,5 +26,22 @@ console.log(JSON.stringify(fields));
|
|
|
25
26
|
await fs.writeFile(extractor_code_path, extractor_code);
|
|
26
27
|
const { stdout } = await exec("node", [extractor_code_path]);
|
|
27
28
|
await fs.unlink(extractor_code_path);
|
|
28
|
-
|
|
29
|
+
const ret = JSON.parse(stdout) as unknown;
|
|
30
|
+
if (
|
|
31
|
+
!is(
|
|
32
|
+
ret,
|
|
33
|
+
predicates.array(
|
|
34
|
+
predicates.shape({
|
|
35
|
+
name: predicates.string,
|
|
36
|
+
type: predicates.string,
|
|
37
|
+
})
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
"Encountered a problem while extracting the names of fields from collection. Got: " +
|
|
43
|
+
stdout
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return ret;
|
|
29
47
|
}
|
package/src/utils/translate.ts
CHANGED
package/src/utils/utils.ts
CHANGED
|
@@ -2,9 +2,11 @@ export type Awaited<T> = T extends Promise<infer U> ? U : T;
|
|
|
2
2
|
export type UnwrapArray<T> = T extends Array<infer U> ? U : T;
|
|
3
3
|
|
|
4
4
|
export function keys<S extends string>(object: Record<S, unknown>): S[] {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
|
|
5
6
|
return Object.keys(object) as any;
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
10
|
export type ExtractTail<T extends any[]> = T extends [infer _, ...infer Tail]
|
|
9
11
|
? Tail
|
|
10
12
|
: never;
|
package/test.cjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const mri = require("mri");
|
|
2
|
+
const { spawn } = require("child_process");
|
|
3
|
+
|
|
4
|
+
const argv = process.argv.slice(2);
|
|
5
|
+
const args = mri(argv);
|
|
6
|
+
|
|
7
|
+
const bin_dir = "./node_modules/.bin/";
|
|
8
|
+
|
|
9
|
+
const mocha = bin_dir + "mocha";
|
|
10
|
+
|
|
11
|
+
let mocha_options = [
|
|
12
|
+
"--recursive",
|
|
13
|
+
"--timeout=10000",
|
|
14
|
+
"--require",
|
|
15
|
+
"source-map-support/register",
|
|
16
|
+
"--exit",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
if (args["test-report"]) {
|
|
20
|
+
mocha_options = [
|
|
21
|
+
...mocha_options,
|
|
22
|
+
// "--require",
|
|
23
|
+
// "ts-node/register",
|
|
24
|
+
// "--require",
|
|
25
|
+
// "./src/http/type-overrides.ts",
|
|
26
|
+
"--reporter",
|
|
27
|
+
"xunit",
|
|
28
|
+
"--reporter-option",
|
|
29
|
+
"output=.xunit",
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const mocha_files = ["lib/**/*.test.js"];
|
|
34
|
+
|
|
35
|
+
let command = [mocha, ...mocha_options, ...mocha_files];
|
|
36
|
+
|
|
37
|
+
if (args.debug) {
|
|
38
|
+
command = ["node", "inspect", ...command];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log("spawning mocha...", command.join(" "));
|
|
42
|
+
|
|
43
|
+
const proc = spawn(command[0], command.slice(1), {
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
env: process.env,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
proc.on("exit", function (code) {
|
|
49
|
+
if (args["test-report"]) {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
} else {
|
|
52
|
+
process.exit(code);
|
|
53
|
+
}
|
|
54
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"module": "
|
|
4
|
-
"moduleResolution": "
|
|
3
|
+
"module": "nodenext",
|
|
4
|
+
"moduleResolution": "nodenext",
|
|
5
5
|
"noImplicitAny": true,
|
|
6
6
|
"noImplicitThis": true,
|
|
7
7
|
"strictNullChecks": true,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"declaration": true,
|
|
10
10
|
"esModuleInterop": true,
|
|
11
11
|
"lib": ["ES6", "ESNext"],
|
|
12
|
-
"outDir": "
|
|
12
|
+
"outDir": "lib",
|
|
13
13
|
"checkJs": false,
|
|
14
14
|
"allowJs": true,
|
|
15
15
|
"declarationDir": "@types",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"sourceMap": true,
|
|
18
18
|
"allowSyntheticDefaultImports": true,
|
|
19
19
|
"skipLibCheck": true,
|
|
20
|
-
"types": ["vitest/globals"]
|
|
21
20
|
},
|
|
22
21
|
|
|
23
22
|
"include": ["src/**/*"],
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Page } from "@sealcode/sealgen";
|
|
2
|
-
import html from "../html.js";
|
|
3
|
-
const actionName = "Sample";
|
|
4
|
-
var sample_page_default = new class SamplePage extends Page {
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6
|
-
async canAccess(_) {
|
|
7
|
-
return { canAccess: true, message: "" };
|
|
8
|
-
}
|
|
9
|
-
async render(ctx) {
|
|
10
|
-
return html(ctx, "Sample", /* @__PURE__ */ React.createElement("div", null));
|
|
11
|
-
}
|
|
12
|
-
}();
|
|
13
|
-
export {
|
|
14
|
-
actionName,
|
|
15
|
-
sample_page_default as default
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=sample.page.js.map
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { withProdApp } from "../test_utils/with-prod-app.js";
|
|
2
|
-
import { VERY_LONG_TEST_TIMEOUT, webhintURL } from "../test_utils/webhint.js";
|
|
3
|
-
import { SampleURL } from "./urls.js";
|
|
4
|
-
import { getBrowser } from "../test_utils/browser-creator.js";
|
|
5
|
-
describe("Sample webhint", () => {
|
|
6
|
-
it(
|
|
7
|
-
"doesn't crash",
|
|
8
|
-
async function() {
|
|
9
|
-
return withProdApp(async ({ base_url, rest_api }) => {
|
|
10
|
-
await rest_api.get(SampleURL);
|
|
11
|
-
await webhintURL(base_url + SampleURL);
|
|
12
|
-
});
|
|
13
|
-
},
|
|
14
|
-
VERY_LONG_TEST_TIMEOUT
|
|
15
|
-
);
|
|
16
|
-
});
|
|
17
|
-
describe("Sample", () => {
|
|
18
|
-
let page;
|
|
19
|
-
let browser;
|
|
20
|
-
let context;
|
|
21
|
-
beforeEach(async () => {
|
|
22
|
-
browser = await getBrowser();
|
|
23
|
-
context = await browser.newContext();
|
|
24
|
-
page = await context.newPage();
|
|
25
|
-
});
|
|
26
|
-
afterEach(async () => {
|
|
27
|
-
await context.close();
|
|
28
|
-
});
|
|
29
|
-
it(
|
|
30
|
-
"works as expected",
|
|
31
|
-
async function() {
|
|
32
|
-
return withProdApp(async ({ base_url }) => {
|
|
33
|
-
await page.goto(base_url + SampleURL);
|
|
34
|
-
});
|
|
35
|
-
},
|
|
36
|
-
VERY_LONG_TEST_TIMEOUT
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
//# sourceMappingURL=sample.test.js.map
|