@antelopejs/dms 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +36 -26
- package/bin/control/base.mjs +9 -4
- package/bin/control/browser-client.mjs +24 -5
- package/bin/control/browser-wire.mjs +41 -0
- package/bin/control/browser-wire.test.mjs +96 -0
- package/bin/control/browser.mjs +78 -27
- package/bin/control/commands-stack.mjs +58 -8
- package/bin/control/commands-verify.mjs +47 -1
- package/bin/control/doctor.mjs +49 -4
- package/bin/control/failure.mjs +9 -0
- package/bin/control/journey.mjs +163 -30
- package/bin/control/journey.test.mjs +61 -0
- package/bin/control/json.mjs +229 -0
- package/bin/control/json.test.mjs +130 -0
- package/bin/control/mail.mjs +57 -7
- package/bin/control/mail.test.mjs +55 -0
- package/bin/control/mongo.mjs +205 -27
- package/bin/control/mongo.test.mjs +298 -0
- package/bin/control/options.mjs +33 -0
- package/bin/control/processes.mjs +24 -2
- package/bin/control/renderer.mjs +39 -0
- package/bin/control/renderer.test.mjs +324 -0
- package/bin/control/run-state.mjs +119 -6
- package/bin/control/run-state.test.mjs +171 -0
- package/bin/control/session.mjs +31 -14
- package/bin/control/stack.mjs +96 -24
- package/bin/control/tab.mjs +117 -18
- package/bin/control/verify/behaviour.mjs +44 -6
- package/bin/control/verify/behaviour.test.mjs +130 -22
- package/bin/control/verify/decode.mjs +220 -0
- package/bin/control/verify/decode.test.mjs +245 -0
- package/bin/control/verify/doors.mjs +1 -1
- package/bin/control/verify/doors.test.mjs +40 -0
- package/bin/control/verify/emit.mjs +135 -9
- package/bin/control/verify/formal.mjs +61 -10
- package/bin/control/verify/formal.test.mjs +19 -3
- package/bin/control/verify/judgement.mjs +22 -3
- package/bin/control/verify/map.mjs +25 -5
- package/bin/control/verify/publish.mjs +20 -14
- package/bin/control/verify/seeded.mjs +65 -16
- package/bin/control/verify/skill.mjs +27 -6
- package/bin/control/verify/types.mjs +29 -0
- package/bin/control/verify/verdict.mjs +72 -7
- package/bin/dms-control.mjs +8 -28
- package/checks/api-error-presentation.d.mts +61 -0
- package/dist/attachments/access.js +1 -1
- package/dist/attachments/access.js.map +1 -1
- package/dist/attachments/fields.d.ts +2 -2
- package/dist/attachments/fields.js +26 -13
- package/dist/attachments/fields.js.map +1 -1
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/attachments/index.js.map +1 -1
- package/dist/attachments/read.js.map +1 -1
- package/dist/attachments/save.d.ts +1 -1
- package/dist/attachments/save.js +49 -17
- package/dist/attachments/save.js.map +1 -1
- package/dist/attachments/table-view.d.ts +1 -2
- package/dist/attachments/table-view.js +23 -13
- package/dist/attachments/table-view.js.map +1 -1
- package/dist/automation/events.d.ts +10 -10
- package/dist/automation/index.js +3 -4
- package/dist/automation/index.js.map +1 -1
- package/dist/automation/send-notification.d.ts +2 -2
- package/dist/automation/send-notification.js +4 -6
- package/dist/automation/send-notification.js.map +1 -1
- package/dist/config/internal/state.d.ts +7 -0
- package/dist/config/internal/state.js +9 -0
- package/dist/config/internal/state.js.map +1 -1
- package/dist/config/startup-config.d.ts +1 -0
- package/dist/config/startup-config.js +11 -0
- package/dist/config/startup-config.js.map +1 -1
- package/dist/crons/cleanup-user-invites.js.map +1 -1
- package/dist/crons/sweep-expired-sessions.js.map +1 -1
- package/dist/crons/sweep-stale-exports.js.map +1 -1
- package/dist/db/guard-row.d.ts +2 -3
- package/dist/db/guard-row.js +1 -2
- package/dist/db/guard-row.js.map +1 -1
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/models/attachments.model.d.ts +7 -5
- package/dist/db/models/index.d.ts +1 -0
- package/dist/db/models/index.js +1 -0
- package/dist/db/models/index.js.map +1 -1
- package/dist/db/models/setupToken.model.d.ts +31 -0
- package/dist/db/models/setupToken.model.js +62 -0
- package/dist/db/models/setupToken.model.js.map +1 -0
- package/dist/db/models/signInAttempts.model.d.ts +7 -5
- package/dist/db/models/systemState.model.d.ts +7 -5
- package/dist/db/models/userEmailChanges.model.d.ts +7 -5
- package/dist/db/models/userKnownDevices.model.d.ts +7 -5
- package/dist/db/models/userNotificationPreferences.model.d.ts +7 -5
- package/dist/db/models/userNotifications.model.d.ts +9 -6
- package/dist/db/models/userNotifications.model.js.map +1 -1
- package/dist/db/setup-token.d.ts +2 -0
- package/dist/db/setup-token.js +28 -0
- package/dist/db/setup-token.js.map +1 -0
- package/dist/db/tables/index.d.ts +1 -0
- package/dist/db/tables/index.js +1 -0
- package/dist/db/tables/index.js.map +1 -1
- package/dist/db/tables/setupToken.table.d.ts +7 -0
- package/dist/db/tables/setupToken.table.js +34 -0
- package/dist/db/tables/setupToken.table.js.map +1 -0
- package/dist/dev/module-update-notifications.js.map +1 -1
- package/dist/hooks/internal/sign-in-monitor.js +6 -6
- package/dist/hooks/internal/sign-in-monitor.js.map +1 -1
- package/dist/implementations/dms/page/controller.d.ts +2 -2
- package/dist/implementations/dms/page/controller.js +3 -1
- package/dist/implementations/dms/page/controller.js.map +1 -1
- package/dist/implementations/dms/page/dynamic-menu.js +3 -2
- package/dist/implementations/dms/page/dynamic-menu.js.map +1 -1
- package/dist/implementations/dms/page/notifications.d.ts +6 -2
- package/dist/implementations/dms/page/notifications.js +1 -0
- package/dist/implementations/dms/page/notifications.js.map +1 -1
- package/dist/implementations/dms/page/payloads.js +6 -10
- package/dist/implementations/dms/page/payloads.js.map +1 -1
- package/dist/implementations/dms/page/preview-pages.js +6 -3
- package/dist/implementations/dms/page/preview-pages.js.map +1 -1
- package/dist/implementations/dms/page/public-layout.d.ts +2 -0
- package/dist/implementations/dms/page/public-layout.js +45 -0
- package/dist/implementations/dms/page/public-layout.js.map +1 -0
- package/dist/implementations/dms/page/registry.js +10 -4
- package/dist/implementations/dms/page/registry.js.map +1 -1
- package/dist/implementations/dms/permissions.js +12 -12
- package/dist/implementations/dms/permissions.js.map +1 -1
- package/dist/implementations/dms-base/bulk-selection.d.ts +3 -4
- package/dist/implementations/dms-base/bulk-selection.js +18 -6
- package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
- package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
- package/dist/implementations/dms-base/footer-summary.js +44 -27
- package/dist/implementations/dms-base/footer-summary.js.map +1 -1
- package/dist/implementations/dms-base/row-rule-operand.d.ts +6 -0
- package/dist/implementations/dms-base/row-rule-operand.js +41 -0
- package/dist/implementations/dms-base/row-rule-operand.js.map +1 -0
- package/dist/implementations/dms-base/search-route.d.ts +9 -13
- package/dist/implementations/dms-base/search-route.js +7 -6
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/implementations/dms-base/services.d.ts +1 -0
- package/dist/implementations/dms-base/services.js +1 -0
- package/dist/implementations/dms-base/services.js.map +1 -1
- package/dist/implementations/dms-base/table-view.d.ts +5 -6
- package/dist/implementations/dms-base/table-view.js +17 -6
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/implementations/dms-html-render/index.js +16 -6
- package/dist/implementations/dms-html-render/index.js.map +1 -1
- package/dist/implementations/dms-notifications/delivery-guard.d.ts +3 -2
- package/dist/implementations/dms-notifications/delivery-guard.js.map +1 -1
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/public/preview/block-types.d.ts +1 -2
- package/dist/pages/public/preview/block-types.js.map +1 -1
- package/dist/pages/settings/overview/account-summary.js +4 -1
- package/dist/pages/settings/overview/account-summary.js.map +1 -1
- package/dist/pages/settings/users/account-activity-feed.js +3 -2
- package/dist/pages/settings/users/account-activity-feed.js.map +1 -1
- package/dist/pages/settings/users/account-credentials.d.ts +1 -1
- package/dist/pages/settings/users/account-credentials.js +1 -2
- package/dist/pages/settings/users/account-credentials.js.map +1 -1
- package/dist/pages/settings/users/account-data-store.d.ts +1 -1
- package/dist/pages/settings/users/account-data-store.js +1 -3
- package/dist/pages/settings/users/account-data-store.js.map +1 -1
- package/dist/pages/settings/users/current-password.js +1 -1
- package/dist/pages/settings/users/current-password.js.map +1 -1
- package/dist/pages/settings/users/email-change.d.ts +2 -2
- package/dist/pages/settings/users/email-change.js +2 -3
- package/dist/pages/settings/users/email-change.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.d.ts +2 -2
- package/dist/pages/settings/users/invite-extension-routes.js +11 -4
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
- package/dist/pages/settings/users/invites.d.ts +2 -2
- package/dist/pages/settings/users/member-invite-batch.d.ts +2 -2
- package/dist/pages/settings/users/member-invite-batch.js +7 -3
- package/dist/pages/settings/users/member-invite-batch.js.map +1 -1
- package/dist/pages/settings/users/member-roles-field.js +1 -4
- package/dist/pages/settings/users/member-roles-field.js.map +1 -1
- package/dist/pages/settings/users/members.d.ts +2 -3
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.d.ts +2 -3
- package/dist/pages/settings/users/notifications.js +2 -1
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/profile-helpers.d.ts +0 -1
- package/dist/pages/settings/users/profile-helpers.js +0 -17
- package/dist/pages/settings/users/profile-helpers.js.map +1 -1
- package/dist/pages/settings/users/profile.d.ts +3 -4
- package/dist/pages/settings/users/profile.js +12 -12
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/region.d.ts +3 -4
- package/dist/pages/settings/users/region.js.map +1 -1
- package/dist/pages/settings/users/regional-preferences.d.ts +8 -1
- package/dist/pages/settings/users/regional-preferences.js +10 -6
- package/dist/pages/settings/users/regional-preferences.js.map +1 -1
- package/dist/pages/settings/users/role-preview.js +3 -2
- package/dist/pages/settings/users/role-preview.js.map +1 -1
- package/dist/pages/settings/users/roles.d.ts +4 -5
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/pages/settings/users/security.d.ts +9 -12
- package/dist/pages/settings/users/security.js +11 -9
- package/dist/pages/settings/users/security.js.map +1 -1
- package/dist/pages/settings/users/two-factor-operations.d.ts +7 -7
- package/dist/pages/settings/users/two-factor-operations.js +5 -7
- package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
- package/dist/realtime/broker.js.map +1 -1
- package/dist/realtime/heartbeat.js.map +1 -1
- package/dist/realtime/redis-broker.js.map +1 -1
- package/dist/realtime/sessions.js.map +1 -1
- package/dist/realtime/sse.js +1 -2
- package/dist/realtime/sse.js.map +1 -1
- package/dist/routes/auth/forgot-password.d.ts +1 -1
- package/dist/routes/auth/forgot-password.js +1 -3
- package/dist/routes/auth/forgot-password.js.map +1 -1
- package/dist/routes/auth/login.d.ts +2 -2
- package/dist/routes/auth/login.js +8 -4
- package/dist/routes/auth/login.js.map +1 -1
- package/dist/routes/auth/logout.d.ts +1 -1
- package/dist/routes/auth/logout.js +1 -2
- package/dist/routes/auth/logout.js.map +1 -1
- package/dist/routes/auth/oauth/providers.js +30 -6
- package/dist/routes/auth/oauth/providers.js.map +1 -1
- package/dist/routes/auth/oauth/state.js +8 -5
- package/dist/routes/auth/oauth/state.js.map +1 -1
- package/dist/routes/auth/oauth/token-exchange.js +8 -1
- package/dist/routes/auth/oauth/token-exchange.js.map +1 -1
- package/dist/routes/auth/password-hash-upgrade.d.ts +2 -0
- package/dist/routes/auth/password-hash-upgrade.js +22 -0
- package/dist/routes/auth/password-hash-upgrade.js.map +1 -0
- package/dist/routes/auth/refresh.d.ts +1 -1
- package/dist/routes/auth/refresh.js +1 -2
- package/dist/routes/auth/refresh.js.map +1 -1
- package/dist/routes/auth/request-2fa-email.d.ts +1 -1
- package/dist/routes/auth/request-2fa-email.js +1 -2
- package/dist/routes/auth/request-2fa-email.js.map +1 -1
- package/dist/routes/auth/reset-password.d.ts +1 -1
- package/dist/routes/auth/reset-password.js +1 -3
- package/dist/routes/auth/reset-password.js.map +1 -1
- package/dist/routes/auth/session-handoff.d.ts +1 -1
- package/dist/routes/auth/session-handoff.js +1 -2
- package/dist/routes/auth/session-handoff.js.map +1 -1
- package/dist/routes/auth/signup.d.ts +1 -1
- package/dist/routes/auth/signup.js +1 -2
- package/dist/routes/auth/signup.js.map +1 -1
- package/dist/routes/auth/switch-tenant.d.ts +2 -3
- package/dist/routes/auth/switch-tenant.js +3 -2
- package/dist/routes/auth/switch-tenant.js.map +1 -1
- package/dist/routes/auth/validate-forgot-password-token.d.ts +1 -1
- package/dist/routes/auth/validate-forgot-password-token.js +1 -3
- package/dist/routes/auth/validate-forgot-password-token.js.map +1 -1
- package/dist/routes/auth/validate-invite-token.d.ts +1 -1
- package/dist/routes/auth/validate-invite-token.js +1 -3
- package/dist/routes/auth/validate-invite-token.js.map +1 -1
- package/dist/routes/auth/verify-2fa.d.ts +1 -1
- package/dist/routes/auth/verify-2fa.js +1 -2
- package/dist/routes/auth/verify-2fa.js.map +1 -1
- package/dist/routes/auth/verify-email.d.ts +1 -1
- package/dist/routes/auth/verify-email.js +1 -2
- package/dist/routes/auth/verify-email.js.map +1 -1
- package/dist/routes/auth.d.ts +14 -15
- package/dist/routes/auth.js +15 -13
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/authentication.d.ts +2 -0
- package/dist/routes/authentication.js +2 -0
- package/dist/routes/authentication.js.map +1 -1
- package/dist/routes/files.d.ts +1 -2
- package/dist/routes/files.js.map +1 -1
- package/dist/routes/onboarding.d.ts +4 -3
- package/dist/routes/onboarding.js +20 -3
- package/dist/routes/onboarding.js.map +1 -1
- package/dist/routes/realtime.js +5 -6
- package/dist/routes/realtime.js.map +1 -1
- package/dist/routes/store.d.ts +6 -7
- package/dist/routes/store.js +19 -13
- package/dist/routes/store.js.map +1 -1
- package/dist/store/catalog.d.ts +13 -5
- package/dist/store/catalog.js +19 -9
- package/dist/store/catalog.js.map +1 -1
- package/dist/store/compatibility.d.ts +1 -0
- package/dist/store/compatibility.js +24 -8
- package/dist/store/compatibility.js.map +1 -1
- package/dist/store/frontend-compatibility.js +15 -3
- package/dist/store/frontend-compatibility.js.map +1 -1
- package/dist/store/interfaces.js +35 -13
- package/dist/store/interfaces.js.map +1 -1
- package/dist/store/internal/frontend/frontend-modules.js +7 -3
- package/dist/store/internal/frontend/frontend-modules.js.map +1 -1
- package/dist/store/internal/frontend/frontend-ready.d.ts +2 -1
- package/dist/store/internal/frontend/frontend-ready.js +6 -4
- package/dist/store/internal/frontend/frontend-ready.js.map +1 -1
- package/dist/store/internal/frontend/manifest.js +6 -7
- package/dist/store/internal/frontend/manifest.js.map +1 -1
- package/dist/store/operations.js.map +1 -1
- package/dist/store/registry.js.map +1 -1
- package/dist/store/runtime.d.ts +13 -0
- package/dist/store/runtime.js +9 -1
- package/dist/store/runtime.js.map +1 -1
- package/dist/utils/admin-invite-email.js +3 -1
- package/dist/utils/admin-invite-email.js.map +1 -1
- package/dist/utils/email-subjects.js +3 -1
- package/dist/utils/email-subjects.js.map +1 -1
- package/dist/utils/ensure-default-tenant.d.ts +2 -1
- package/dist/utils/ensure-default-tenant.js +126 -8
- package/dist/utils/ensure-default-tenant.js.map +1 -1
- package/dist/utils/export-jobs-run.js.map +1 -1
- package/dist/utils/export-jobs.js +3 -9
- package/dist/utils/export-jobs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/own-value.d.ts +1 -0
- package/dist/utils/own-value.js +7 -0
- package/dist/utils/own-value.js.map +1 -0
- package/dist/utils/row-action-rule-evaluator.js +31 -35
- package/dist/utils/row-action-rule-evaluator.js.map +1 -1
- package/dist/utils/sign-in-country.js +4 -2
- package/dist/utils/sign-in-country.js.map +1 -1
- package/dist/utils/tenant-export-archive.js +9 -14
- package/dist/utils/tenant-export-archive.js.map +1 -1
- package/dist/utils/user-agent.js +2 -1
- package/dist/utils/user-agent.js.map +1 -1
- package/dist/validation/auth.schema.d.ts +10 -10
- package/dist/validation/onboarding/register-admin.schema.d.ts +5 -0
- package/dist/validation/onboarding/register-admin.schema.js +3 -0
- package/dist/validation/onboarding/register-admin.schema.js.map +1 -1
- package/dist/validation/regional-preferences.schema.d.ts +2 -2
- package/dist/validation/role-editor.schema.d.ts +1 -1
- package/frontend-vue/build/backend-boundary.ts +49 -18
- package/frontend-vue/dms.frontend.ts +8 -12
- package/frontend-vue/layers/dms-auth/server/accounts.ts +44 -3
- package/frontend-vue/layers/dms-auth/server/actions.ts +17 -4
- package/frontend-vue/layers/dms-auth/server/oauth.ts +42 -4
- package/frontend-vue/layers/dms-core/app/types/watch.ts +1 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +1 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +1 -0
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +5 -0
- package/frontend-vue/layers/dms-layout/app/app.config.ts +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +9 -9
- package/frontend-vue/layers/dms-layout/app/components/layout/AppLogo.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useInstalledModules.ts +78 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +5 -24
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +2 -1
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +2 -1
- package/frontend-vue/layers/dms-onboarding/app/components/onboarding/steps/register.vue +21 -1
- package/frontend-vue/layers/dms-onboarding/app/custom-pages/onboarding.vue +14 -0
- package/frontend-vue/layers/dms-onboarding/i18n/locales/onboarding-en-GB.json +2 -0
- package/frontend-vue/layers/dms-onboarding/i18n/locales/onboarding-fr-FR.json +2 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +57 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +7 -9
- package/frontend-vue/layers/dms-ui/app/entries/composables/form/registerFormFunctions.ts +1 -0
- package/frontend-vue/lazy-plugin.ts +18 -0
- package/frontend-vue/package.json +2 -2
- package/package.json +19 -13
- package/skills/dms-project/SKILL.md +4 -3
package/bin/control/session.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { firstString, stringField } from "./json.mjs";
|
|
4
5
|
import { saveRun } from "./run-state.mjs";
|
|
5
6
|
|
|
6
7
|
const REQUEST_TIMEOUT_MS = 30_000;
|
|
@@ -9,11 +10,15 @@ const PASSWORD_BYTES = 9;
|
|
|
9
10
|
const OK_MIN = 200;
|
|
10
11
|
const OK_MAX = 299;
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {{ status: number | null, body: unknown, error: string | null }} ApiAnswer
|
|
15
|
+
*/
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* The credentials of the run's administrator, derived from the run id so a
|
|
14
19
|
* journey can name them before onboarding.
|
|
15
20
|
*
|
|
16
|
-
* @param {
|
|
21
|
+
* @param {Pick<import("./run-state.mjs").RunState, "id" | "adminPassword">} state The run
|
|
17
22
|
* @returns {{ email: string, password: string, firstName: string, lastName: string }} The administrator's credentials
|
|
18
23
|
*/
|
|
19
24
|
export function adminCredentials(state) {
|
|
@@ -35,6 +40,10 @@ export function newPassword() {
|
|
|
35
40
|
return `Vr1!${randomBytes(PASSWORD_BYTES).toString("base64url").replace(/[-_]/g, "x")}`;
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} text The text
|
|
45
|
+
* @returns {string} The text as a folder name
|
|
46
|
+
*/
|
|
38
47
|
function slug(text) {
|
|
39
48
|
return text
|
|
40
49
|
.toLowerCase()
|
|
@@ -63,9 +72,15 @@ export function nextFolder(state, group, label) {
|
|
|
63
72
|
return folder;
|
|
64
73
|
}
|
|
65
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} text The response text
|
|
77
|
+
* @returns {unknown} The parsed JSON, or the text when it is not JSON
|
|
78
|
+
*/
|
|
66
79
|
function parseBody(text) {
|
|
67
80
|
try {
|
|
68
|
-
|
|
81
|
+
/** @type {unknown} */
|
|
82
|
+
const parsed = JSON.parse(text);
|
|
83
|
+
return parsed;
|
|
69
84
|
} catch {
|
|
70
85
|
return text;
|
|
71
86
|
}
|
|
@@ -76,8 +91,8 @@ function parseBody(text) {
|
|
|
76
91
|
* response.json into `folder`. Secrets in the request are left out.
|
|
77
92
|
*
|
|
78
93
|
* @param {string} folder Where the evidence goes
|
|
79
|
-
* @param {{ url: string, method: string, body?:
|
|
80
|
-
* @returns {Promise<
|
|
94
|
+
* @param {{ url: string, method: string, body?: unknown, token?: string }} request What to send
|
|
95
|
+
* @returns {Promise<ApiAnswer>} The answer
|
|
81
96
|
*/
|
|
82
97
|
export async function recordedRequest(folder, request) {
|
|
83
98
|
const headers = { accept: "application/json" };
|
|
@@ -127,9 +142,9 @@ export function isOk(answer) {
|
|
|
127
142
|
* Signs an account in through `POST /api/auth/login` and keeps its tokens in
|
|
128
143
|
* the run state.
|
|
129
144
|
*
|
|
130
|
-
* @param {
|
|
145
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
131
146
|
* @param {string} account The account name, `admin` for the run's administrator
|
|
132
|
-
* @returns {Promise<
|
|
147
|
+
* @returns {Promise<ApiAnswer>} The login answer
|
|
133
148
|
*/
|
|
134
149
|
export async function signIn(state, account) {
|
|
135
150
|
const credentials = state.accounts?.[account];
|
|
@@ -144,8 +159,9 @@ export async function signIn(state, account) {
|
|
|
144
159
|
method: "POST",
|
|
145
160
|
body: { email: credentials.email, password: credentials.password },
|
|
146
161
|
});
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
const token = stringField(answer.body, "access_token");
|
|
163
|
+
if (isOk(answer) && token) {
|
|
164
|
+
state.tokens = { ...state.tokens, [account]: token };
|
|
149
165
|
saveRun(state);
|
|
150
166
|
}
|
|
151
167
|
return answer;
|
|
@@ -155,9 +171,9 @@ export async function signIn(state, account) {
|
|
|
155
171
|
* Creates the run's administrator through `POST /api/onboarding/register`,
|
|
156
172
|
* then signs it in.
|
|
157
173
|
*
|
|
158
|
-
* @param {
|
|
174
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
159
175
|
* @param {{ platformName?: string }} options Wizard details
|
|
160
|
-
* @returns {Promise<{ register:
|
|
176
|
+
* @returns {Promise<{ register: ApiAnswer, login: ApiAnswer | null }>} Both answers
|
|
161
177
|
*/
|
|
162
178
|
export async function onboard(state, options) {
|
|
163
179
|
const admin = adminCredentials(state);
|
|
@@ -168,6 +184,7 @@ export async function onboard(state, options) {
|
|
|
168
184
|
method: "POST",
|
|
169
185
|
body: {
|
|
170
186
|
...admin,
|
|
187
|
+
setupToken: state.secrets.DMS_SETUP_TOKEN,
|
|
171
188
|
platformName: options.platformName ?? `Verify ${state.id}`,
|
|
172
189
|
},
|
|
173
190
|
},
|
|
@@ -178,7 +195,7 @@ export async function onboard(state, options) {
|
|
|
178
195
|
admin: {
|
|
179
196
|
email: admin.email,
|
|
180
197
|
password: admin.password,
|
|
181
|
-
userId: register.body
|
|
198
|
+
userId: firstString(register.body) ?? null,
|
|
182
199
|
},
|
|
183
200
|
};
|
|
184
201
|
saveRun(state);
|
|
@@ -189,9 +206,9 @@ export async function onboard(state, options) {
|
|
|
189
206
|
* An authenticated (or anonymous) request to the run's backend, signing the
|
|
190
207
|
* account in again once when its token was refused.
|
|
191
208
|
*
|
|
192
|
-
* @param {
|
|
193
|
-
* @param {{ method: string, path: string, body?:
|
|
194
|
-
* @returns {Promise<
|
|
209
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
210
|
+
* @param {{ method: string, path: string, body?: unknown, as: string, label?: string, folder?: () => string }} request What to send and as whom; `as` is an account name or `anon`; `folder` makes each attempt's evidence folder
|
|
211
|
+
* @returns {Promise<ApiAnswer & { folder: string }>} The answer and its evidence folder
|
|
195
212
|
*/
|
|
196
213
|
export async function apiRequest(state, request) {
|
|
197
214
|
const label = request.label ?? `${request.method} ${request.path}`;
|
package/bin/control/stack.mjs
CHANGED
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
14
|
+
import { describeFailure } from "./failure.mjs";
|
|
14
15
|
import { checkoutOf } from "./git.mjs";
|
|
16
|
+
import { stringField } from "./json.mjs";
|
|
15
17
|
import { dropDatabase, dropRunDatabases, startMongod } from "./mongo.mjs";
|
|
16
18
|
import {
|
|
17
19
|
freePort,
|
|
@@ -29,8 +31,14 @@ import {
|
|
|
29
31
|
runsRoot,
|
|
30
32
|
saveRun,
|
|
31
33
|
} from "./run-state.mjs";
|
|
34
|
+
import { RENDERER_CLI, resolveRenderer } from "./renderer.mjs";
|
|
32
35
|
import { newPassword } from "./session.mjs";
|
|
33
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {import("./run-state.mjs").RunState} RunState
|
|
39
|
+
* @typedef {{ playground: string, renderer?: string, runs?: string, mongoUrl?: string, env?: string[], base?: string, skill?: string }} UpOptions Where the playground and the runs are
|
|
40
|
+
*/
|
|
41
|
+
|
|
34
42
|
const READY_TIMEOUT_MS = 180_000;
|
|
35
43
|
const SECRET_BYTES = 32;
|
|
36
44
|
const MAIL_PORT_ATTEMPTS = 3;
|
|
@@ -42,6 +50,7 @@ const SECRET_NAMES = [
|
|
|
42
50
|
"DMS_BOOTSTRAP_SECRET",
|
|
43
51
|
"DMS_HTML_RENDER_SECRET",
|
|
44
52
|
"DMS_SESSION_SECRET",
|
|
53
|
+
"DMS_SETUP_TOKEN",
|
|
45
54
|
];
|
|
46
55
|
const DROPPED_PREFIXES = ["ANTELOPEJS_", "npm_", "PNPM_", "DMS_", "MONGO_"];
|
|
47
56
|
|
|
@@ -66,23 +75,30 @@ export function resolutionFolders(state) {
|
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/**
|
|
69
|
-
* The `ajs` and `ajs dms` entry points the playground installs
|
|
78
|
+
* The `ajs` and `ajs dms` entry points the playground installs, `ajs dms`
|
|
79
|
+
* being the renderer checkout's when the run serves one.
|
|
70
80
|
*
|
|
71
81
|
* @param {string} playground The playground folder
|
|
82
|
+
* @param {string} [renderer] The renderer checkout the run serves
|
|
72
83
|
* @returns {{ ajs: string, dms: string }} Their scripts
|
|
73
84
|
*/
|
|
74
|
-
export function cliPaths(playground) {
|
|
85
|
+
export function cliPaths(playground, renderer) {
|
|
75
86
|
const require = createRequire(join(playground, "package.json"));
|
|
76
87
|
const core = dirname(require.resolve("@antelopejs/core/package.json"));
|
|
77
|
-
const frontend =
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
const frontend =
|
|
89
|
+
renderer ??
|
|
90
|
+
dirname(require.resolve("@antelopejs/dms-frontend/package.json"));
|
|
80
91
|
return {
|
|
81
92
|
ajs: join(core, "dist", "core", "cli", "index.js"),
|
|
82
|
-
dms: join(frontend,
|
|
93
|
+
dms: join(frontend, RENDERER_CLI),
|
|
83
94
|
};
|
|
84
95
|
}
|
|
85
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {string} real
|
|
99
|
+
* @param {string} shadow
|
|
100
|
+
* @param {string} keep
|
|
101
|
+
*/
|
|
86
102
|
function shadowLevel(real, shadow, keep) {
|
|
87
103
|
mkdirSync(shadow, { recursive: true });
|
|
88
104
|
for (const entry of readdirSync(real)) {
|
|
@@ -91,19 +107,40 @@ function shadowLevel(real, shadow, keep) {
|
|
|
91
107
|
}
|
|
92
108
|
}
|
|
93
109
|
|
|
110
|
+
/**
|
|
111
|
+
* A `node_modules` that is `modules` with `@antelopejs/dms-frontend` replaced
|
|
112
|
+
* by the renderer checkout, every other entry linked to the original.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} modules The playground's installed modules
|
|
115
|
+
* @param {string} shadow The run's `node_modules`
|
|
116
|
+
* @param {string} renderer The renderer checkout
|
|
117
|
+
*/
|
|
118
|
+
export function linkRenderer(modules, shadow, renderer) {
|
|
119
|
+
const scope = "@antelopejs";
|
|
120
|
+
shadowLevel(modules, shadow, scope);
|
|
121
|
+
const scoped = join(modules, scope);
|
|
122
|
+
const shadowScope = join(shadow, scope);
|
|
123
|
+
if (existsSync(scoped)) shadowLevel(scoped, shadowScope, "dms-frontend");
|
|
124
|
+
else mkdirSync(shadowScope);
|
|
125
|
+
symlinkSync(renderer, join(shadowScope, "dms-frontend"));
|
|
126
|
+
}
|
|
127
|
+
|
|
94
128
|
/**
|
|
95
129
|
* Lays out the run's copy of the checkout: from the checkout root down to
|
|
96
130
|
* the playground, every sibling is linked, so relative paths such as a
|
|
97
131
|
* tsconfig `extends` resolve as in the checkout. The playground itself is
|
|
98
132
|
* copied without its installed modules and build, so `.antelope/` and the
|
|
99
|
-
* frontend build belong to the run.
|
|
133
|
+
* frontend build belong to the run. Its `node_modules` is the playground's,
|
|
134
|
+
* except that `@antelopejs/dms-frontend` is the `renderer` checkout when
|
|
135
|
+
* there is one.
|
|
100
136
|
*
|
|
101
137
|
* @param {string} playground The playground folder
|
|
102
138
|
* @param {string} checkout The checkout root, or the module folder outside one
|
|
103
139
|
* @param {string} root The run's copy of the checkout
|
|
140
|
+
* @param {string} [renderer] The renderer checkout the run serves
|
|
104
141
|
* @returns {string} The run's playground folder
|
|
105
142
|
*/
|
|
106
|
-
export function layOutCopy(playground, checkout, root) {
|
|
143
|
+
export function layOutCopy(playground, checkout, root, renderer) {
|
|
107
144
|
const segments = relative(checkout, playground).split(sep);
|
|
108
145
|
let real = checkout;
|
|
109
146
|
let shadow = root;
|
|
@@ -116,7 +153,9 @@ export function layOutCopy(playground, checkout, root) {
|
|
|
116
153
|
recursive: true,
|
|
117
154
|
filter: (source) => !COPY_SKIPPED.has(basename(source)),
|
|
118
155
|
});
|
|
119
|
-
|
|
156
|
+
const modules = join(playground, "node_modules");
|
|
157
|
+
if (renderer) linkRenderer(modules, join(shadow, "node_modules"), renderer);
|
|
158
|
+
else symlinkSync(modules, join(shadow, "node_modules"));
|
|
120
159
|
return shadow;
|
|
121
160
|
}
|
|
122
161
|
|
|
@@ -133,7 +172,7 @@ function inheritedEnv() {
|
|
|
133
172
|
* database, origin and secrets, an npm cache and a pnpm store shared by
|
|
134
173
|
* every run, no update checks and no colors. Extra `KEY=VALUE` pairs from `--env` come last.
|
|
135
174
|
*
|
|
136
|
-
* @param {
|
|
175
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
137
176
|
* @returns {NodeJS.ProcessEnv} The environment
|
|
138
177
|
*/
|
|
139
178
|
export function runEnv(state) {
|
|
@@ -162,6 +201,10 @@ export function runEnv(state) {
|
|
|
162
201
|
};
|
|
163
202
|
}
|
|
164
203
|
|
|
204
|
+
/**
|
|
205
|
+
* @param {string[]} pairs
|
|
206
|
+
* @returns {Record<string, string>}
|
|
207
|
+
*/
|
|
165
208
|
function parseExtraEnv(pairs) {
|
|
166
209
|
return Object.fromEntries(
|
|
167
210
|
pairs.map((pair) => {
|
|
@@ -172,6 +215,10 @@ function parseExtraEnv(pairs) {
|
|
|
172
215
|
);
|
|
173
216
|
}
|
|
174
217
|
|
|
218
|
+
/**
|
|
219
|
+
* @param {UpOptions} options
|
|
220
|
+
* @returns {Promise<RunState>}
|
|
221
|
+
*/
|
|
175
222
|
async function initialState(options) {
|
|
176
223
|
const playground = resolve(options.playground);
|
|
177
224
|
if (!existsSync(join(playground, "antelope.config.ts"))) {
|
|
@@ -183,7 +230,9 @@ async function initialState(options) {
|
|
|
183
230
|
const webPort = await freePort();
|
|
184
231
|
const smtpPort = await freePort();
|
|
185
232
|
const checkout = checkoutOf(playground);
|
|
186
|
-
|
|
233
|
+
const renderer = options.renderer && resolveRenderer(options.renderer);
|
|
234
|
+
const verified = renderer ? checkoutOf(renderer) : checkout;
|
|
235
|
+
const state = {
|
|
187
236
|
...paths,
|
|
188
237
|
id,
|
|
189
238
|
createdAt: new Date().toISOString(),
|
|
@@ -191,7 +240,7 @@ async function initialState(options) {
|
|
|
191
240
|
playground,
|
|
192
241
|
moduleRoot: dirname(playground),
|
|
193
242
|
repo: checkout.root,
|
|
194
|
-
headSha:
|
|
243
|
+
headSha: verified.head,
|
|
195
244
|
baseRef: options.base ?? "origin/main",
|
|
196
245
|
skill: options.skill ?? null,
|
|
197
246
|
apiPort,
|
|
@@ -217,13 +266,15 @@ async function initialState(options) {
|
|
|
217
266
|
tokens: {},
|
|
218
267
|
ready: false,
|
|
219
268
|
};
|
|
269
|
+
if (renderer) state.renderer = renderer;
|
|
270
|
+
return state;
|
|
220
271
|
}
|
|
221
272
|
|
|
222
273
|
/**
|
|
223
274
|
* How to run one of the playground's CLIs for the run: its own log, the
|
|
224
275
|
* run's environment, from the run's playground copy or `cwd`.
|
|
225
276
|
*
|
|
226
|
-
* @param {
|
|
277
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
227
278
|
* @param {string} name The log name
|
|
228
279
|
* @param {string} script The CLI script
|
|
229
280
|
* @param {string[]} args Its arguments
|
|
@@ -241,6 +292,10 @@ export function spec(state, name, script, args, cwd = state.runPlayground) {
|
|
|
241
292
|
};
|
|
242
293
|
}
|
|
243
294
|
|
|
295
|
+
/**
|
|
296
|
+
* @param {RunState} state
|
|
297
|
+
* @param {import("./run-state.mjs").ProcessRecord} processRecord
|
|
298
|
+
*/
|
|
244
299
|
function record(state, processRecord) {
|
|
245
300
|
state.processes = [
|
|
246
301
|
...state.processes.filter((entry) => entry.name !== processRecord.name),
|
|
@@ -253,7 +308,7 @@ function record(state, processRecord) {
|
|
|
253
308
|
* Starts the run's mail sink unless it already runs, and waits until it is
|
|
254
309
|
* the process listening on the run's mail port.
|
|
255
310
|
*
|
|
256
|
-
* @param {
|
|
311
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
257
312
|
* @returns {Promise<void>} Resolves once it listens
|
|
258
313
|
*/
|
|
259
314
|
export async function ensureMail(state) {
|
|
@@ -271,6 +326,10 @@ export async function ensureMail(state) {
|
|
|
271
326
|
await waitForOwnPort(state.smtpPort, mail, READY_TIMEOUT_MS);
|
|
272
327
|
}
|
|
273
328
|
|
|
329
|
+
/**
|
|
330
|
+
* @param {RunState} state
|
|
331
|
+
* @returns {Promise<void>}
|
|
332
|
+
*/
|
|
274
333
|
async function startFirstMail(state) {
|
|
275
334
|
for (let attempt = 1; ; attempt += 1) {
|
|
276
335
|
try {
|
|
@@ -283,6 +342,11 @@ async function startFirstMail(state) {
|
|
|
283
342
|
}
|
|
284
343
|
}
|
|
285
344
|
|
|
345
|
+
/**
|
|
346
|
+
* @param {RunState} state
|
|
347
|
+
* @param {{ playground?: string, cwd?: string }} [build]
|
|
348
|
+
* @returns {Promise<void>}
|
|
349
|
+
*/
|
|
286
350
|
async function startBackend(state, build) {
|
|
287
351
|
const { ajs } = cliPaths(build?.playground ?? state.playground);
|
|
288
352
|
const backend = startDetached(
|
|
@@ -302,12 +366,17 @@ async function startBackend(state, build) {
|
|
|
302
366
|
);
|
|
303
367
|
}
|
|
304
368
|
|
|
369
|
+
/**
|
|
370
|
+
* @param {RunState} state
|
|
371
|
+
* @returns {Promise<void>}
|
|
372
|
+
*/
|
|
305
373
|
async function buildAndStart(state) {
|
|
306
|
-
const clis = cliPaths(state.playground);
|
|
374
|
+
const clis = cliPaths(state.playground, state.renderer);
|
|
307
375
|
state.runPlayground = layOutCopy(
|
|
308
376
|
state.playground,
|
|
309
377
|
state.repo ?? state.moduleRoot,
|
|
310
378
|
state.root,
|
|
379
|
+
state.renderer,
|
|
311
380
|
);
|
|
312
381
|
if (state.ownsMongo) {
|
|
313
382
|
const mongo = await startMongod(state, resolutionFolders(state));
|
|
@@ -345,12 +414,13 @@ async function buildAndStart(state) {
|
|
|
345
414
|
/**
|
|
346
415
|
* Starts an isolated DMS from a playground: a copy of it in a run folder,
|
|
347
416
|
* free ports, the `docs` environment, a `verify_<run>` database on a mongod
|
|
348
|
-
* the run owns (or `--mongo-url`), fresh secrets and a mail sink.
|
|
417
|
+
* the run owns (or `--mongo-url`), fresh secrets and a mail sink. With
|
|
418
|
+
* `renderer`, the dashboard is that built dms-frontend checkout. Ready once
|
|
349
419
|
* `/api/onboarding/informations` and `/dms-preview` answer 200. On failure
|
|
350
420
|
* it stops what it started and keeps the logs.
|
|
351
421
|
*
|
|
352
|
-
* @param {
|
|
353
|
-
* @returns {Promise<
|
|
422
|
+
* @param {UpOptions} options Where the playground and the runs are
|
|
423
|
+
* @returns {Promise<import("./run-state.mjs").RunState>} The ready run
|
|
354
424
|
*/
|
|
355
425
|
export async function up(options) {
|
|
356
426
|
const state = await initialState(options);
|
|
@@ -373,7 +443,7 @@ export async function up(options) {
|
|
|
373
443
|
* a crash would, from the run's build or another one (`build`, the base
|
|
374
444
|
* version's). The frontend keeps serving and proxies to the same port.
|
|
375
445
|
*
|
|
376
|
-
* @param {
|
|
446
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
377
447
|
* @param {{ playground: string, cwd: string }} [build] Another build to start
|
|
378
448
|
* @returns {Promise<void>} Resolves once the backend answers again
|
|
379
449
|
*/
|
|
@@ -389,7 +459,7 @@ export async function restartBackend(state, build) {
|
|
|
389
459
|
* journey database is dropped, so a long run does not exhaust mongod's
|
|
390
460
|
* file handles; its evidence stays.
|
|
391
461
|
*
|
|
392
|
-
* @param {
|
|
462
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
393
463
|
* @param {string} suffix What tells the database apart, such as `j03`
|
|
394
464
|
* @returns {Promise<string>} The database now in use
|
|
395
465
|
*/
|
|
@@ -411,7 +481,7 @@ export async function freshDatabase(state, suffix) {
|
|
|
411
481
|
* run's databases, and removes everything but `evidence/`, `logs/` and
|
|
412
482
|
* `run.json`, from which the secrets and passwords are removed.
|
|
413
483
|
*
|
|
414
|
-
* @param {
|
|
484
|
+
* @param {import("./run-state.mjs").RunState} state The run
|
|
415
485
|
* @returns {Promise<string[]>} One line per action
|
|
416
486
|
*/
|
|
417
487
|
export async function down(state) {
|
|
@@ -428,8 +498,8 @@ export async function down(state) {
|
|
|
428
498
|
state.mongoUrl,
|
|
429
499
|
state.databasePrefix,
|
|
430
500
|
resolutionFolders(state),
|
|
431
|
-
).catch((error) => [
|
|
432
|
-
`could not drop the run's databases: ${error
|
|
501
|
+
).catch((/** @type {unknown} */ error) => [
|
|
502
|
+
`could not drop the run's databases: ${describeFailure(error)}`,
|
|
433
503
|
]);
|
|
434
504
|
lines.push(...dropped);
|
|
435
505
|
}
|
|
@@ -473,7 +543,9 @@ export async function down(state) {
|
|
|
473
543
|
*/
|
|
474
544
|
export function packageVersion(file) {
|
|
475
545
|
try {
|
|
476
|
-
|
|
546
|
+
/** @type {unknown} */
|
|
547
|
+
const manifest = JSON.parse(readFileSync(file, "utf8"));
|
|
548
|
+
return stringField(manifest, "version") ?? "missing";
|
|
477
549
|
} catch {
|
|
478
550
|
return "missing";
|
|
479
551
|
}
|
package/bin/control/tab.mjs
CHANGED
|
@@ -5,10 +5,22 @@ import {
|
|
|
5
5
|
signInThroughForm,
|
|
6
6
|
} from "./browser.mjs";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import("./browser.mjs").Action} Action
|
|
10
|
+
* @typedef {import("./browser.mjs").Outcome} Outcome
|
|
11
|
+
* @typedef {import("./browser.mjs").Target} Target
|
|
12
|
+
* @typedef {import("./journey.mjs").Check} Check
|
|
13
|
+
* @typedef {{ timeoutMs?: number }} Timeout
|
|
14
|
+
*/
|
|
15
|
+
|
|
8
16
|
const EXPECT_TIMEOUT_MS = 15_000;
|
|
9
17
|
const POLL_MS = 250;
|
|
10
18
|
const DETAIL_LENGTH = 300;
|
|
11
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} text The text
|
|
22
|
+
* @returns {string} The text on one line, cut to the detail length
|
|
23
|
+
*/
|
|
12
24
|
function flatten(text) {
|
|
13
25
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
14
26
|
return flat.length > DETAIL_LENGTH
|
|
@@ -25,7 +37,7 @@ function flatten(text) {
|
|
|
25
37
|
export class Tab {
|
|
26
38
|
/**
|
|
27
39
|
* @param {import("./journey.mjs").JourneyContext} context The journey
|
|
28
|
-
* @param {
|
|
40
|
+
* @param {import("playwright-core").Page} page The Playwright page
|
|
29
41
|
* @param {string} label The tab's name in evidence
|
|
30
42
|
*/
|
|
31
43
|
constructor(context, page, label) {
|
|
@@ -37,9 +49,9 @@ export class Tab {
|
|
|
37
49
|
/**
|
|
38
50
|
* Runs one browser action and records it as a critical step.
|
|
39
51
|
*
|
|
40
|
-
* @param {
|
|
52
|
+
* @param {Omit<Action, "baseUrl">} action The action, as `perform` takes it
|
|
41
53
|
* @param {string} label What the step does
|
|
42
|
-
* @returns {Promise<
|
|
54
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
43
55
|
*/
|
|
44
56
|
async act(action, label) {
|
|
45
57
|
const folder = this.context.step(`${this.label} ${label}`);
|
|
@@ -56,22 +68,44 @@ export class Tab {
|
|
|
56
68
|
return { ...outcome, folder };
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Opens a path of the frontend. Callers pass link lookups that are
|
|
73
|
+
* `undefined` when no link matched; the navigation then targets the
|
|
74
|
+
* literal path `undefined`.
|
|
75
|
+
*
|
|
76
|
+
* @param {string | undefined} path The path
|
|
77
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
78
|
+
*/
|
|
60
79
|
goto(path) {
|
|
61
80
|
return this.act({ do: "goto", path }, `open ${path}`);
|
|
62
81
|
}
|
|
63
82
|
|
|
64
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Reloads the page.
|
|
85
|
+
*
|
|
86
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
87
|
+
*/
|
|
65
88
|
reload() {
|
|
66
89
|
return this.act({ do: "reload" }, "reload");
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Clicks a target found by role and name.
|
|
94
|
+
*
|
|
95
|
+
* @param {Target} target What to click
|
|
96
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
97
|
+
*/
|
|
70
98
|
click(target) {
|
|
71
99
|
return this.act({ do: "click", target }, `click ${describeTarget(target)}`);
|
|
72
100
|
}
|
|
73
101
|
|
|
74
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Types into a target found by role and name, replacing its value.
|
|
104
|
+
*
|
|
105
|
+
* @param {Target} target What to fill
|
|
106
|
+
* @param {string} value The new value
|
|
107
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
108
|
+
*/
|
|
75
109
|
fill(target, value) {
|
|
76
110
|
return this.act(
|
|
77
111
|
{ do: "fill", target, value },
|
|
@@ -79,7 +113,13 @@ export class Tab {
|
|
|
79
113
|
);
|
|
80
114
|
}
|
|
81
115
|
|
|
82
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Chooses an option of a native select by its label.
|
|
118
|
+
*
|
|
119
|
+
* @param {Target} target The select
|
|
120
|
+
* @param {string} value The option label
|
|
121
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
122
|
+
*/
|
|
83
123
|
select(target, value) {
|
|
84
124
|
return this.act(
|
|
85
125
|
{ do: "select", target, value },
|
|
@@ -87,23 +127,44 @@ export class Tab {
|
|
|
87
127
|
);
|
|
88
128
|
}
|
|
89
129
|
|
|
90
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Ticks a checkbox or switch.
|
|
132
|
+
*
|
|
133
|
+
* @param {Target} target The checkbox or switch
|
|
134
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
135
|
+
*/
|
|
91
136
|
check(target) {
|
|
92
137
|
return this.act({ do: "check", target }, `check ${describeTarget(target)}`);
|
|
93
138
|
}
|
|
94
139
|
|
|
95
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Presses a key, on a target or on the page.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} key The key
|
|
144
|
+
* @param {Target} [target] Where to press it
|
|
145
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
146
|
+
*/
|
|
96
147
|
press(key, target) {
|
|
97
148
|
const where = target ? ` in ${describeTarget(target)}` : "";
|
|
98
149
|
return this.act({ do: "press", key, target }, `press ${key}${where}`);
|
|
99
150
|
}
|
|
100
151
|
|
|
101
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* Records the page as it is now.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} label What the step records
|
|
156
|
+
* @returns {Promise<Outcome & { folder: string }>} The outcome
|
|
157
|
+
*/
|
|
102
158
|
snapshot(label) {
|
|
103
159
|
return this.act({ do: "snapshot" }, label);
|
|
104
160
|
}
|
|
105
161
|
|
|
106
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Signs an account of the journey in through the sign-in form.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} account The account name
|
|
166
|
+
* @returns {Promise<Outcome | { ok: boolean, url: string, error: string }>} The outcome of the last sign-in step, or a failure without a snapshot when the journey has no such account
|
|
167
|
+
*/
|
|
107
168
|
async signIn(account) {
|
|
108
169
|
const credentials = this.context.state.accounts?.[account];
|
|
109
170
|
const outcome = credentials
|
|
@@ -127,7 +188,12 @@ export class Tab {
|
|
|
127
188
|
return outcome;
|
|
128
189
|
}
|
|
129
190
|
|
|
130
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* The Playwright locator of a target.
|
|
193
|
+
*
|
|
194
|
+
* @param {Target} target What to find
|
|
195
|
+
* @returns {import("playwright-core").Locator} The locator
|
|
196
|
+
*/
|
|
131
197
|
locator(target) {
|
|
132
198
|
return locate(this.page, target);
|
|
133
199
|
}
|
|
@@ -135,6 +201,10 @@ export class Tab {
|
|
|
135
201
|
/**
|
|
136
202
|
* A check that a target becomes visible, or with `{ hidden: true }`
|
|
137
203
|
* hidden or absent, within the timeout.
|
|
204
|
+
*
|
|
205
|
+
* @param {Target} target What to wait for
|
|
206
|
+
* @param {Timeout & { hidden?: boolean }} [options] Whether to wait for it to be hidden, and how long
|
|
207
|
+
* @returns {Promise<Check>} The check
|
|
138
208
|
*/
|
|
139
209
|
async visible(target, options = {}) {
|
|
140
210
|
const state = options.hidden ? "hidden" : "visible";
|
|
@@ -152,7 +222,13 @@ export class Tab {
|
|
|
152
222
|
};
|
|
153
223
|
}
|
|
154
224
|
|
|
155
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* A check that a target is enabled (or, with `{ disabled: true }`, disabled).
|
|
227
|
+
*
|
|
228
|
+
* @param {Target} target The control
|
|
229
|
+
* @param {Timeout & { disabled?: boolean }} [options] Whether to expect it disabled, and how long
|
|
230
|
+
* @returns {Promise<Check>} The check
|
|
231
|
+
*/
|
|
156
232
|
async enabled(target, options = {}) {
|
|
157
233
|
const element = this.locator(target).first();
|
|
158
234
|
const disabled = await element
|
|
@@ -166,7 +242,13 @@ export class Tab {
|
|
|
166
242
|
};
|
|
167
243
|
}
|
|
168
244
|
|
|
169
|
-
/**
|
|
245
|
+
/**
|
|
246
|
+
* A check that the page shows (or, with `{ not: true }`, never shows) text matching `pattern`.
|
|
247
|
+
*
|
|
248
|
+
* @param {RegExp} pattern The text to match
|
|
249
|
+
* @param {Timeout & { not?: boolean }} [options] Whether the text must never show, and how long
|
|
250
|
+
* @returns {Promise<Check>} The check
|
|
251
|
+
*/
|
|
170
252
|
async shows(pattern, options = {}) {
|
|
171
253
|
const deadline = Date.now() + (options.timeoutMs ?? EXPECT_TIMEOUT_MS);
|
|
172
254
|
let text = "";
|
|
@@ -185,7 +267,13 @@ export class Tab {
|
|
|
185
267
|
};
|
|
186
268
|
}
|
|
187
269
|
|
|
188
|
-
/**
|
|
270
|
+
/**
|
|
271
|
+
* A check that the current path matches `pattern` within the timeout.
|
|
272
|
+
*
|
|
273
|
+
* @param {RegExp} pattern The path to match
|
|
274
|
+
* @param {Timeout} [options] How long to wait
|
|
275
|
+
* @returns {Promise<Check>} The check
|
|
276
|
+
*/
|
|
189
277
|
async at(pattern, options = {}) {
|
|
190
278
|
const reached = await this.page
|
|
191
279
|
.waitForURL((url) => pattern.test(url.pathname), {
|
|
@@ -202,7 +290,13 @@ export class Tab {
|
|
|
202
290
|
};
|
|
203
291
|
}
|
|
204
292
|
|
|
205
|
-
/**
|
|
293
|
+
/**
|
|
294
|
+
* A check on how many targets match.
|
|
295
|
+
*
|
|
296
|
+
* @param {Target} target What to count
|
|
297
|
+
* @param {number} expected How many should match
|
|
298
|
+
* @returns {Promise<Check>} The check
|
|
299
|
+
*/
|
|
206
300
|
async count(target, expected) {
|
|
207
301
|
const actual = await this.locator(target).count();
|
|
208
302
|
return {
|
|
@@ -212,7 +306,12 @@ export class Tab {
|
|
|
212
306
|
};
|
|
213
307
|
}
|
|
214
308
|
|
|
215
|
-
/**
|
|
309
|
+
/**
|
|
310
|
+
* The current value of a form field.
|
|
311
|
+
*
|
|
312
|
+
* @param {Target} target The form field
|
|
313
|
+
* @returns {Promise<string>} The value
|
|
314
|
+
*/
|
|
216
315
|
value(target) {
|
|
217
316
|
return this.locator(target)
|
|
218
317
|
.first()
|