@sealcode/sealgen 0.15.1 → 0.15.3
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/lib/index.js +25 -0
- 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 +37 -19
- package/{dist → lib}/templates/jdd-component.js +2 -2
- package/{dist → lib}/utils/extract-fields-from-collection.js +16 -1
- package/package.json +19 -14
- 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 +53 -24
- 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}/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>`;
|
|
@@ -110,8 +121,9 @@ export abstract class StatefulPage<
|
|
|
110
121
|
...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
111
122
|
) {
|
|
112
123
|
const { action } = this.extractActionAndLabel(action_description);
|
|
113
|
-
return `./?action=${action}&action_args=${
|
|
114
|
-
|
|
124
|
+
return `./?action=${action}&action_args=${encodeURIComponent(
|
|
125
|
+
// encoding as URI Component because sometimes it can contain a "+" which is treated as a space
|
|
126
|
+
to_base64(JSON.stringify(args))
|
|
115
127
|
)}`;
|
|
116
128
|
}
|
|
117
129
|
|
|
@@ -120,7 +132,8 @@ export abstract class StatefulPage<
|
|
|
120
132
|
action_description: StatefulPageActionDescription<ActionName>,
|
|
121
133
|
...args: ExtractTail<ExtractTail<Parameters<Actions[ActionName]>>>
|
|
122
134
|
) {
|
|
123
|
-
|
|
135
|
+
const { label, content } =
|
|
136
|
+
this.extractActionAndLabel(action_description);
|
|
124
137
|
return /* HTML */ `
|
|
125
138
|
<button
|
|
126
139
|
type="submit"
|
|
@@ -156,15 +169,27 @@ export abstract class StatefulPage<
|
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
async preprocessOverrides(
|
|
159
|
-
|
|
172
|
+
_state: State,
|
|
160
173
|
values: Record<string, unknown>
|
|
161
174
|
): Promise<Record<string, unknown>> {
|
|
162
175
|
return values;
|
|
163
176
|
}
|
|
164
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
|
+
|
|
165
189
|
async extractState(
|
|
166
190
|
ctx: BaseContext
|
|
167
191
|
): Promise<StateAndMetadata<State, Actions>> {
|
|
192
|
+
console.log("stateful-page.ts:188", "EXTRACT STATE");
|
|
168
193
|
if (
|
|
169
194
|
!hasShape(
|
|
170
195
|
{
|
|
@@ -188,7 +213,8 @@ export abstract class StatefulPage<
|
|
|
188
213
|
// the "$" key is parsed as dot notation and overrides the state
|
|
189
214
|
|
|
190
215
|
const original_state_string = ctx.$body.state;
|
|
191
|
-
const original_state =
|
|
216
|
+
const original_state = await this.deserializeState(
|
|
217
|
+
ctx,
|
|
192
218
|
typeof original_state_string == "string"
|
|
193
219
|
? from_base64(original_state_string)
|
|
194
220
|
: "{}"
|
|
@@ -201,6 +227,7 @@ export abstract class StatefulPage<
|
|
|
201
227
|
state_overrides
|
|
202
228
|
);
|
|
203
229
|
let modified_state = deepmerge(original_state, state_overrides, {
|
|
230
|
+
isMergeableObject: isPlainObject,
|
|
204
231
|
arrayMerge: (
|
|
205
232
|
target: any[],
|
|
206
233
|
source: any[],
|
|
@@ -208,7 +235,7 @@ export abstract class StatefulPage<
|
|
|
208
235
|
) => {
|
|
209
236
|
// https://github.com/TehShrike/deepmerge?tab=readme-ov-file#arraymerge-example-combine-arrays
|
|
210
237
|
const destination = target.slice();
|
|
211
|
-
|
|
238
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
212
239
|
source.forEach((item, index) => {
|
|
213
240
|
if (typeof destination[index] === "undefined") {
|
|
214
241
|
destination[index] =
|
|
@@ -217,15 +244,18 @@ export abstract class StatefulPage<
|
|
|
217
244
|
options
|
|
218
245
|
);
|
|
219
246
|
} else if (options.isMergeableObject(item)) {
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
220
248
|
destination[index] = deepmerge(
|
|
221
249
|
target[index],
|
|
222
250
|
item,
|
|
223
251
|
options
|
|
224
252
|
);
|
|
225
253
|
} else if (target.indexOf(item) === -1) {
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
226
255
|
destination[index] = item;
|
|
227
256
|
}
|
|
228
257
|
});
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
229
259
|
return destination;
|
|
230
260
|
},
|
|
231
261
|
}) as State;
|
|
@@ -236,7 +266,7 @@ export abstract class StatefulPage<
|
|
|
236
266
|
if (ctx.$body.action && ctx.$body.action_args) {
|
|
237
267
|
return {
|
|
238
268
|
state: modified_state,
|
|
239
|
-
action: ctx.$body.action
|
|
269
|
+
action: ctx.$body.action,
|
|
240
270
|
inputs,
|
|
241
271
|
action_args: ctx.$body.action_args,
|
|
242
272
|
};
|
|
@@ -260,17 +290,16 @@ export abstract class StatefulPage<
|
|
|
260
290
|
const { action, state, inputs, action_args } =
|
|
261
291
|
await this.extractState(ctx);
|
|
262
292
|
if (action) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
from_base64(action_args as string)
|
|
270
|
-
) as unknown[])
|
|
271
|
-
),
|
|
272
|
-
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[])
|
|
273
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);
|
|
274
303
|
} else {
|
|
275
304
|
ctx.body = this.render(ctx, state, inputs);
|
|
276
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
|