@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
|
@@ -8,6 +8,11 @@ import { emitFingerprint } from "./emit.mjs";
|
|
|
8
8
|
const DETAIL_LIMIT = 5;
|
|
9
9
|
const OUTPUT_LIMIT = 64 * 1024 * 1024;
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} repo The checkout
|
|
13
|
+
* @param {string} sha The commit
|
|
14
|
+
* @returns {string} The scratch worktree
|
|
15
|
+
*/
|
|
11
16
|
function worktreeAt(repo, sha) {
|
|
12
17
|
const dir = mkdtempSync(join(tmpdir(), "dms-control-behaviour-"));
|
|
13
18
|
git(["worktree", "add", "--quiet", "--detach", "--force", dir, sha], repo);
|
|
@@ -21,8 +26,9 @@ function worktreeAt(repo, sha) {
|
|
|
21
26
|
*
|
|
22
27
|
* @param {string} repo The checkout
|
|
23
28
|
* @param {string} sha The commit
|
|
24
|
-
* @param {(dir: string) =>
|
|
25
|
-
* @returns {
|
|
29
|
+
* @param {(dir: string) => Result} action What to run there
|
|
30
|
+
* @returns {Result} What `action` returned
|
|
31
|
+
* @template Result
|
|
26
32
|
*/
|
|
27
33
|
export function inWorktree(repo, sha, action) {
|
|
28
34
|
const dir = worktreeAt(repo, sha);
|
|
@@ -34,6 +40,12 @@ export function inWorktree(repo, sha, action) {
|
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} repo The checkout
|
|
45
|
+
* @param {string[]} command The probe command
|
|
46
|
+
* @param {string} sha The commit
|
|
47
|
+
* @returns {string[]} The lines the probe printed
|
|
48
|
+
*/
|
|
37
49
|
function probeFingerprint(repo, command, sha) {
|
|
38
50
|
const [program, ...args] = command;
|
|
39
51
|
return inWorktree(repo, sha, (dir) => {
|
|
@@ -50,7 +62,13 @@ function probeFingerprint(repo, command, sha) {
|
|
|
50
62
|
});
|
|
51
63
|
}
|
|
52
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @param {{ structure: string, text: string }[]} left The items
|
|
67
|
+
* @param {{ structure: string, text: string }[]} right The items to subtract
|
|
68
|
+
* @returns {{ structure: string, text: string }[]} The items of `left` that `right` lacks
|
|
69
|
+
*/
|
|
53
70
|
function difference(left, right) {
|
|
71
|
+
/** @type {Map<string, number>} */
|
|
54
72
|
const counts = new Map();
|
|
55
73
|
for (const item of right)
|
|
56
74
|
counts.set(item.structure, (counts.get(item.structure) ?? 0) + 1);
|
|
@@ -62,6 +80,12 @@ function difference(left, right) {
|
|
|
62
80
|
});
|
|
63
81
|
}
|
|
64
82
|
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} path The covered file
|
|
85
|
+
* @param {{ structure: string, text: string }[]} base Its statements at the base
|
|
86
|
+
* @param {{ structure: string, text: string }[]} head Its statements at HEAD
|
|
87
|
+
* @returns {{ removed: string[], added: string[] }} The first statement that differs
|
|
88
|
+
*/
|
|
65
89
|
function sequenceChanges(path, base, head) {
|
|
66
90
|
const length = Math.max(base.length, head.length);
|
|
67
91
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -79,6 +103,11 @@ function sequenceChanges(path, base, head) {
|
|
|
79
103
|
return { removed: [], added: [] };
|
|
80
104
|
}
|
|
81
105
|
|
|
106
|
+
/**
|
|
107
|
+
* @param {ReturnType<typeof emitFingerprint>} base The fingerprint at the base
|
|
108
|
+
* @param {ReturnType<typeof emitFingerprint>} head The fingerprint at HEAD
|
|
109
|
+
* @returns {{ removed: string[], added: string[] }} What differs
|
|
110
|
+
*/
|
|
82
111
|
function compareEmit(base, head) {
|
|
83
112
|
const removed = difference(base.functions, head.functions).map(
|
|
84
113
|
(item) => `hoisted ${item.text}`,
|
|
@@ -101,7 +130,16 @@ function compareEmit(base, head) {
|
|
|
101
130
|
return { removed, added };
|
|
102
131
|
}
|
|
103
132
|
|
|
133
|
+
/**
|
|
134
|
+
* @param {string[]} base The probe lines at the base
|
|
135
|
+
* @param {string[]} head The probe lines at HEAD
|
|
136
|
+
* @returns {{ removed: string[], added: string[] }} What differs
|
|
137
|
+
*/
|
|
104
138
|
function compareProbe(base, head) {
|
|
139
|
+
/**
|
|
140
|
+
* @param {string[]} list Probe lines
|
|
141
|
+
* @returns {{ structure: string, text: string }[]} The lines as items
|
|
142
|
+
*/
|
|
105
143
|
const lines = (list) => list.map((line) => ({ structure: line, text: line }));
|
|
106
144
|
if (JSON.stringify(base) === JSON.stringify(head))
|
|
107
145
|
return { removed: [], added: [] };
|
|
@@ -119,10 +157,10 @@ function compareProbe(base, head) {
|
|
|
119
157
|
* function declarations stay the same wherever they live; for `probe`, the
|
|
120
158
|
* check's probe prints the same lines on a checkout of the base and of HEAD.
|
|
121
159
|
*
|
|
122
|
-
* @param {
|
|
123
|
-
* @param {
|
|
124
|
-
* @param {
|
|
125
|
-
* @returns {
|
|
160
|
+
* @param {import("./skill.mjs").Skill} skill The skill
|
|
161
|
+
* @param {Pick<import("./types.mjs").FormalCheck, "covers" | "behaviour">} check The formal check
|
|
162
|
+
* @param {import("./types.mjs").Commits} commits The two commits
|
|
163
|
+
* @returns {import("./types.mjs").BehaviourChange} The decision and what differs
|
|
126
164
|
*/
|
|
127
165
|
export function behaviourChange(skill, check, commits) {
|
|
128
166
|
const probe =
|
|
@@ -9,10 +9,19 @@ import { behaviourChange } from "./behaviour.mjs";
|
|
|
9
9
|
const COVERS = ["src/a.ts", "src/b.ts"];
|
|
10
10
|
const IMPORT_OUTPUT = 'import { FAILURE, SUCCESS } from "./output";\n';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} repo
|
|
14
|
+
* @param {...string} args
|
|
15
|
+
*/
|
|
12
16
|
function git(repo, ...args) {
|
|
13
17
|
return execFileSync("git", args, { cwd: repo, encoding: "utf8" }).trim();
|
|
14
18
|
}
|
|
15
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} repo
|
|
22
|
+
* @param {Record<string, string>} files
|
|
23
|
+
* @param {string} message
|
|
24
|
+
*/
|
|
16
25
|
function commit(repo, files, message) {
|
|
17
26
|
for (const [path, content] of Object.entries(files)) {
|
|
18
27
|
mkdirSync(dirname(join(repo, path)), { recursive: true });
|
|
@@ -35,6 +44,12 @@ function commit(repo, files, message) {
|
|
|
35
44
|
return git(repo, "rev-parse", "HEAD");
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {import("node:test").TestContext} t
|
|
49
|
+
* @param {Record<string, string>} base
|
|
50
|
+
* @param {Record<string, string>} head
|
|
51
|
+
* @param {string[]} [covers]
|
|
52
|
+
*/
|
|
38
53
|
function changed(t, base, head, covers = COVERS) {
|
|
39
54
|
const repo = mkdtempSync(join(tmpdir(), "dms-control-emit-test-"));
|
|
40
55
|
t.after(() => rmSync(repo, { recursive: true, force: true }));
|
|
@@ -50,7 +65,7 @@ const OUTPUT = {
|
|
|
50
65
|
"src/output.ts": "export const FAILURE = 1;\nexport const SUCCESS = 0;\n",
|
|
51
66
|
};
|
|
52
67
|
|
|
53
|
-
test("a function declaration moved verbatim to another covered file is unchanged", (t) => {
|
|
68
|
+
await test("a function declaration moved verbatim to another covered file is unchanged", (t) => {
|
|
54
69
|
assert.equal(
|
|
55
70
|
changed(
|
|
56
71
|
t,
|
|
@@ -64,7 +79,7 @@ test("a function declaration moved verbatim to another covered file is unchanged
|
|
|
64
79
|
);
|
|
65
80
|
});
|
|
66
81
|
|
|
67
|
-
test("an import that moves with its use to another covered file is unchanged", (t) => {
|
|
82
|
+
await test("an import that moves with its use to another covered file is unchanged", (t) => {
|
|
68
83
|
const use = `${IMPORT_OUTPUT}export function code() {\n return FAILURE + SUCCESS;\n}\n`;
|
|
69
84
|
assert.equal(
|
|
70
85
|
changed(
|
|
@@ -80,7 +95,7 @@ test("an import that moves with its use to another covered file is unchanged", (
|
|
|
80
95
|
);
|
|
81
96
|
});
|
|
82
97
|
|
|
83
|
-
test("a comment, a type annotation and a reformat are unchanged", (t) => {
|
|
98
|
+
await test("a comment, a type annotation and a reformat are unchanged", (t) => {
|
|
84
99
|
assert.equal(
|
|
85
100
|
changed(
|
|
86
101
|
t,
|
|
@@ -94,14 +109,14 @@ test("a comment, a type annotation and a reformat are unchanged", (t) => {
|
|
|
94
109
|
);
|
|
95
110
|
});
|
|
96
111
|
|
|
97
|
-
test("single quotes against double quotes are unchanged", (t) => {
|
|
112
|
+
await test("single quotes against double quotes are unchanged", (t) => {
|
|
98
113
|
assert.equal(
|
|
99
114
|
changed(t, { "src/a.ts": "run('x');\n" }, { "src/a.ts": 'run("x");\n' }),
|
|
100
115
|
false,
|
|
101
116
|
);
|
|
102
117
|
});
|
|
103
118
|
|
|
104
|
-
test("a body edit is changed", (t) => {
|
|
119
|
+
await test("a body edit is changed", (t) => {
|
|
105
120
|
assert.equal(
|
|
106
121
|
changed(
|
|
107
122
|
t,
|
|
@@ -112,7 +127,7 @@ test("a body edit is changed", (t) => {
|
|
|
112
127
|
);
|
|
113
128
|
});
|
|
114
129
|
|
|
115
|
-
test("swapping two statements of a covered file is changed", (t) => {
|
|
130
|
+
await test("swapping two statements of a covered file is changed", (t) => {
|
|
116
131
|
assert.equal(
|
|
117
132
|
changed(
|
|
118
133
|
t,
|
|
@@ -123,7 +138,7 @@ test("swapping two statements of a covered file is changed", (t) => {
|
|
|
123
138
|
);
|
|
124
139
|
});
|
|
125
140
|
|
|
126
|
-
test("moving a statement that is not a function declaration to another covered file is changed", (t) => {
|
|
141
|
+
await test("moving a statement that is not a function declaration to another covered file is changed", (t) => {
|
|
127
142
|
assert.equal(
|
|
128
143
|
changed(
|
|
129
144
|
t,
|
|
@@ -140,7 +155,7 @@ test("moving a statement that is not a function declaration to another covered f
|
|
|
140
155
|
);
|
|
141
156
|
});
|
|
142
157
|
|
|
143
|
-
test("importing the same name under an alias that points elsewhere is changed", (t) => {
|
|
158
|
+
await test("importing the same name under an alias that points elsewhere is changed", (t) => {
|
|
144
159
|
const use = (specifier) =>
|
|
145
160
|
`import { ${specifier} } from "./output";\nexport function code() {\n return CANCELLED + FAILURE;\n}\n`;
|
|
146
161
|
assert.equal(
|
|
@@ -153,7 +168,7 @@ test("importing the same name under an alias that points elsewhere is changed",
|
|
|
153
168
|
);
|
|
154
169
|
});
|
|
155
170
|
|
|
156
|
-
test("importing a binding from another module is changed", (t) => {
|
|
171
|
+
await test("importing a binding from another module is changed", (t) => {
|
|
157
172
|
const use = (module) =>
|
|
158
173
|
`import { FAILURE } from "${module}";\nexport function code() {\n return FAILURE;\n}\n`;
|
|
159
174
|
assert.equal(
|
|
@@ -166,7 +181,7 @@ test("importing a binding from another module is changed", (t) => {
|
|
|
166
181
|
);
|
|
167
182
|
});
|
|
168
183
|
|
|
169
|
-
test("retargeting a re-export is changed", (t) => {
|
|
184
|
+
await test("retargeting a re-export is changed", (t) => {
|
|
170
185
|
assert.equal(
|
|
171
186
|
changed(
|
|
172
187
|
t,
|
|
@@ -177,7 +192,7 @@ test("retargeting a re-export is changed", (t) => {
|
|
|
177
192
|
);
|
|
178
193
|
});
|
|
179
194
|
|
|
180
|
-
test("removing a side-effect import is changed", (t) => {
|
|
195
|
+
await test("removing a side-effect import is changed", (t) => {
|
|
181
196
|
assert.equal(
|
|
182
197
|
changed(
|
|
183
198
|
t,
|
|
@@ -188,7 +203,7 @@ test("removing a side-effect import is changed", (t) => {
|
|
|
188
203
|
);
|
|
189
204
|
});
|
|
190
205
|
|
|
191
|
-
test("swapping two assignments to the same field is changed", (t) => {
|
|
206
|
+
await test("swapping two assignments to the same field is changed", (t) => {
|
|
192
207
|
assert.equal(
|
|
193
208
|
changed(
|
|
194
209
|
t,
|
|
@@ -199,7 +214,7 @@ test("swapping two assignments to the same field is changed", (t) => {
|
|
|
199
214
|
);
|
|
200
215
|
});
|
|
201
216
|
|
|
202
|
-
test("swapping two registrations pushed to the same list is changed", (t) => {
|
|
217
|
+
await test("swapping two registrations pushed to the same list is changed", (t) => {
|
|
203
218
|
assert.equal(
|
|
204
219
|
changed(
|
|
205
220
|
t,
|
|
@@ -210,7 +225,7 @@ test("swapping two registrations pushed to the same list is changed", (t) => {
|
|
|
210
225
|
);
|
|
211
226
|
});
|
|
212
227
|
|
|
213
|
-
test("importing min instead of max under the same alias, both in covered files, is changed", (t) => {
|
|
228
|
+
await test("importing min instead of max under the same alias, both in covered files, is changed", (t) => {
|
|
214
229
|
const use = (name) =>
|
|
215
230
|
`import { ${name} as pick } from "./b";\nexport function best() {\n return pick(1, 2);\n}\n`;
|
|
216
231
|
const lib = {
|
|
@@ -227,6 +242,11 @@ test("importing min instead of max under the same alias, both in covered files,
|
|
|
227
242
|
);
|
|
228
243
|
});
|
|
229
244
|
|
|
245
|
+
/**
|
|
246
|
+
* @param {import("node:test").TestContext} t
|
|
247
|
+
* @param {Record<string, string>} base
|
|
248
|
+
* @param {Record<string, string>} head
|
|
249
|
+
*/
|
|
230
250
|
function changedAll(t, base, head) {
|
|
231
251
|
return changed(t, base, head, Object.keys(base));
|
|
232
252
|
}
|
|
@@ -234,7 +254,7 @@ function changedAll(t, base, head) {
|
|
|
234
254
|
const OPS =
|
|
235
255
|
"export function add(a, b) {\n return a + b;\n}\nexport function sub(a, b) {\n return a - b;\n}\n";
|
|
236
256
|
|
|
237
|
-
test("retargeting a re-export of a covered module to another of its exports is changed", (t) => {
|
|
257
|
+
await test("retargeting a re-export of a covered module to another of its exports is changed", (t) => {
|
|
238
258
|
assert.equal(
|
|
239
259
|
changedAll(
|
|
240
260
|
t,
|
|
@@ -248,7 +268,7 @@ test("retargeting a re-export of a covered module to another of its exports is c
|
|
|
248
268
|
);
|
|
249
269
|
});
|
|
250
270
|
|
|
251
|
-
test("a re-export of a covered module under its own name, reflowed, is unchanged", (t) => {
|
|
271
|
+
await test("a re-export of a covered module under its own name, reflowed, is unchanged", (t) => {
|
|
252
272
|
assert.equal(
|
|
253
273
|
changedAll(
|
|
254
274
|
t,
|
|
@@ -259,7 +279,7 @@ test("a re-export of a covered module under its own name, reflowed, is unchanged
|
|
|
259
279
|
);
|
|
260
280
|
});
|
|
261
281
|
|
|
262
|
-
test("retargeting a local export alias is changed", (t) => {
|
|
282
|
+
await test("retargeting a local export alias is changed", (t) => {
|
|
263
283
|
const local = (target) =>
|
|
264
284
|
`function f() {\n return 1;\n}\nfunction g() {\n return 2;\n}\nexport { ${target} as run };\n`;
|
|
265
285
|
assert.equal(
|
|
@@ -276,7 +296,7 @@ test("retargeting a local export alias is changed", (t) => {
|
|
|
276
296
|
);
|
|
277
297
|
});
|
|
278
298
|
|
|
279
|
-
test("exporting a local under its own name, by keyword or by list, is unchanged", (t) => {
|
|
299
|
+
await test("exporting a local under its own name, by keyword or by list, is unchanged", (t) => {
|
|
280
300
|
assert.equal(
|
|
281
301
|
changedAll(
|
|
282
302
|
t,
|
|
@@ -287,7 +307,7 @@ test("exporting a local under its own name, by keyword or by list, is unchanged"
|
|
|
287
307
|
);
|
|
288
308
|
});
|
|
289
309
|
|
|
290
|
-
test("swapping two side-effect imports is changed", (t) => {
|
|
310
|
+
await test("swapping two side-effect imports is changed", (t) => {
|
|
291
311
|
assert.equal(
|
|
292
312
|
changedAll(
|
|
293
313
|
t,
|
|
@@ -298,7 +318,7 @@ test("swapping two side-effect imports is changed", (t) => {
|
|
|
298
318
|
);
|
|
299
319
|
});
|
|
300
320
|
|
|
301
|
-
test("a function moved verbatim into a file with a different top-level variable of the same name is changed", (t) => {
|
|
321
|
+
await test("a function moved verbatim into a file with a different top-level variable of the same name is changed", (t) => {
|
|
302
322
|
const log = "export function report() {\n return Logger.channel;\n}\n";
|
|
303
323
|
assert.equal(
|
|
304
324
|
changedAll(
|
|
@@ -318,7 +338,7 @@ test("a function moved verbatim into a file with a different top-level variable
|
|
|
318
338
|
);
|
|
319
339
|
});
|
|
320
340
|
|
|
321
|
-
test("a function moved with the variable it reads, or reading it through an import, is judged by that variable", (t) => {
|
|
341
|
+
await test("a function moved with the variable it reads, or reading it through an import, is judged by that variable", (t) => {
|
|
322
342
|
const log = "export function report() {\n return Logger.channel;\n}\n";
|
|
323
343
|
assert.equal(
|
|
324
344
|
changedAll(
|
|
@@ -336,7 +356,7 @@ test("a function moved with the variable it reads, or reading it through an impo
|
|
|
336
356
|
);
|
|
337
357
|
});
|
|
338
358
|
|
|
339
|
-
test("an import aliased to a name that a local variable also has is changed when a function switches to the local", (t) => {
|
|
359
|
+
await test("an import aliased to a name that a local variable also has is changed when a function switches to the local", (t) => {
|
|
340
360
|
const lib = "export const x = 1;\n";
|
|
341
361
|
const use = (name) =>
|
|
342
362
|
`import { x as y } from "./lib";\nconst x = 2;\nexport function value() {\n return ${name};\n}\n`;
|
|
@@ -349,3 +369,91 @@ test("an import aliased to a name that a local variable also has is changed when
|
|
|
349
369
|
true,
|
|
350
370
|
);
|
|
351
371
|
});
|
|
372
|
+
|
|
373
|
+
const NON_LITERAL_SPECIFIERS = [
|
|
374
|
+
["an identifier", "import x from foo;\nx();\n", "import x from bar;\nx();\n"],
|
|
375
|
+
[
|
|
376
|
+
"a template without substitutions",
|
|
377
|
+
"import x from `./a`;\nx();\n",
|
|
378
|
+
"import x from `./b`;\nx();\n",
|
|
379
|
+
],
|
|
380
|
+
["a numeric literal", "import x from 1;\nx();\n", "import x from 2;\nx();\n"],
|
|
381
|
+
["an export-star identifier", "export * from foo;\n", "export * from bar;\n"],
|
|
382
|
+
[
|
|
383
|
+
"a named re-export identifier",
|
|
384
|
+
"export { a } from foo;\n",
|
|
385
|
+
"export { a } from bar;\n",
|
|
386
|
+
],
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
for (const [kind, base, head] of NON_LITERAL_SPECIFIERS) {
|
|
390
|
+
await test(`retargeting a module specifier written as ${kind} is changed`, (t) => {
|
|
391
|
+
assert.equal(changed(t, { "src/a.ts": base }, { "src/a.ts": head }), true);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
await test("retargeting an identifier specifier in a Vue script is changed", (t) => {
|
|
396
|
+
const vue = (specifier) =>
|
|
397
|
+
`<template><p/></template>\n<script>import x from ${specifier}; x();</script>\n`;
|
|
398
|
+
assert.equal(
|
|
399
|
+
changed(t, { "src/a.vue": vue("foo") }, { "src/a.vue": vue("bar") }, [
|
|
400
|
+
"src/a.vue",
|
|
401
|
+
]),
|
|
402
|
+
true,
|
|
403
|
+
);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const SAME_TEXT_SPECIFIERS = [
|
|
407
|
+
[
|
|
408
|
+
"a bare package against the same name as an identifier",
|
|
409
|
+
'import ts from "typescript";\nts.version;\n',
|
|
410
|
+
"import ts from typescript;\nts.version;\n",
|
|
411
|
+
],
|
|
412
|
+
[
|
|
413
|
+
"a bare package against the same name as a template",
|
|
414
|
+
'import ts from "typescript";\nts.version;\n',
|
|
415
|
+
"import ts from `typescript`;\nts.version;\n",
|
|
416
|
+
],
|
|
417
|
+
[
|
|
418
|
+
"a quoted number against the number",
|
|
419
|
+
'import x from "1";\nx();\n',
|
|
420
|
+
"import x from 1;\nx();\n",
|
|
421
|
+
],
|
|
422
|
+
[
|
|
423
|
+
"a quoted relative path against the same text as a template",
|
|
424
|
+
'import x from "./a";\nx();\n',
|
|
425
|
+
"import x from `./a`;\nx();\n",
|
|
426
|
+
],
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
for (const [kind, base, head] of SAME_TEXT_SPECIFIERS) {
|
|
430
|
+
await test(`${kind} is unchanged`, (t) => {
|
|
431
|
+
assert.equal(changed(t, { "src/a.ts": base }, { "src/a.ts": head }), false);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
await test("a module specifier that is a call expression throws on either commit", (t) => {
|
|
436
|
+
const call = "import x from a();\nx();\n";
|
|
437
|
+
const valid = 'import x from "./a";\nx();\n';
|
|
438
|
+
assert.throws(() => changed(t, { "src/a.ts": valid }, { "src/a.ts": call }), {
|
|
439
|
+
name: "TypeError",
|
|
440
|
+
message: /found CallExpression/,
|
|
441
|
+
});
|
|
442
|
+
assert.throws(() => changed(t, { "src/a.ts": call }, { "src/a.ts": valid }), {
|
|
443
|
+
name: "TypeError",
|
|
444
|
+
message: /found CallExpression/,
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
await test("a module specifier that is a template literal with substitutions throws the named error on either commit", (t) => {
|
|
449
|
+
const template = "import x from `./${name}`;\nx();\n";
|
|
450
|
+
const valid = 'import x from "./a";\nx();\n';
|
|
451
|
+
assert.throws(
|
|
452
|
+
() => changed(t, { "src/a.ts": valid }, { "src/a.ts": template }),
|
|
453
|
+
{ name: "TypeError", message: /found TemplateExpression/ },
|
|
454
|
+
);
|
|
455
|
+
assert.throws(
|
|
456
|
+
() => changed(t, { "src/a.ts": template }, { "src/a.ts": valid }),
|
|
457
|
+
{ name: "TypeError", message: /found TemplateExpression/ },
|
|
458
|
+
);
|
|
459
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
arrayOf,
|
|
4
|
+
decode,
|
|
5
|
+
either,
|
|
6
|
+
instanceOf,
|
|
7
|
+
isBoolean,
|
|
8
|
+
isNumber,
|
|
9
|
+
isString,
|
|
10
|
+
nullable,
|
|
11
|
+
oneOf,
|
|
12
|
+
recordOf,
|
|
13
|
+
objectOf,
|
|
14
|
+
} from "../json.mjs";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Guards for the JSON the verification harness reads back: results it wrote,
|
|
18
|
+
* the verifier's judgement, the statuses GitHub answers and the skill's
|
|
19
|
+
* config. Each is checked against the typedef it narrows to, and a value
|
|
20
|
+
* that does not match is refused with an error that names where it was read.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {import("../json.mjs").Guard<unknown>} AnyGuard
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const strings = arrayOf(isString);
|
|
26
|
+
|
|
27
|
+
const rules = objectOf({
|
|
28
|
+
exitCode: nullable(isNumber),
|
|
29
|
+
violated: strings,
|
|
30
|
+
passing: strings,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/** @type {import("../json.mjs").Guard<import("./types.mjs").FormalResult>} The result `runFormalCheck` records in `result.json` */
|
|
34
|
+
export const isFormalResult = objectOf(
|
|
35
|
+
{
|
|
36
|
+
name: isString,
|
|
37
|
+
exitCode: nullable(isNumber),
|
|
38
|
+
violated: strings,
|
|
39
|
+
passing: strings,
|
|
40
|
+
folder: isString,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
commits: objectOf({ baseSha: isString, headSha: isString }),
|
|
44
|
+
behaviour: objectOf({
|
|
45
|
+
method: isString,
|
|
46
|
+
changed: isBoolean,
|
|
47
|
+
removed: strings,
|
|
48
|
+
added: strings,
|
|
49
|
+
}),
|
|
50
|
+
checkChanged: isBoolean,
|
|
51
|
+
base: nullable(rules),
|
|
52
|
+
fixed: strings,
|
|
53
|
+
missing: strings,
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/** @type {import("../json.mjs").Guard<import("./types.mjs").FeatureResult[]>} The journey results a run records in `<feature>/feature.json` */
|
|
58
|
+
const isFeatureResults = arrayOf(
|
|
59
|
+
objectOf({
|
|
60
|
+
feature: isString,
|
|
61
|
+
journey: isString,
|
|
62
|
+
title: isString,
|
|
63
|
+
entryPoint: isString,
|
|
64
|
+
status: oneOf(["PASS", "FAIL"]),
|
|
65
|
+
database: isString,
|
|
66
|
+
steps: arrayOf(
|
|
67
|
+
objectOf({
|
|
68
|
+
name: isString,
|
|
69
|
+
checks: arrayOf(
|
|
70
|
+
objectOf({ name: isString, pass: isBoolean, detail: isString }),
|
|
71
|
+
),
|
|
72
|
+
}),
|
|
73
|
+
),
|
|
74
|
+
error: nullable(isString),
|
|
75
|
+
folder: isString,
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/** @type {import("../json.mjs").Guard<import("./types.mjs").RawJudgement>} The verifier's `judgement.json` */
|
|
80
|
+
export const isRawJudgement = objectOf(
|
|
81
|
+
{},
|
|
82
|
+
{
|
|
83
|
+
verifier: isString,
|
|
84
|
+
notVerified: strings,
|
|
85
|
+
attention: strings,
|
|
86
|
+
docs: arrayOf(
|
|
87
|
+
objectOf({ page: isString }, { matches: isBoolean, note: isString }),
|
|
88
|
+
),
|
|
89
|
+
doors: objectOf(
|
|
90
|
+
{},
|
|
91
|
+
{
|
|
92
|
+
none: isString,
|
|
93
|
+
rows: arrayOf(
|
|
94
|
+
objectOf(
|
|
95
|
+
{},
|
|
96
|
+
{
|
|
97
|
+
kind: isString,
|
|
98
|
+
location: isString,
|
|
99
|
+
irreversible: isString,
|
|
100
|
+
containment: isString,
|
|
101
|
+
containmentJourney: isString,
|
|
102
|
+
},
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
falsePositives: arrayOf(
|
|
106
|
+
objectOf(
|
|
107
|
+
{},
|
|
108
|
+
{ kind: isString, location: isString, reason: isString },
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
/** What `gh api repos/<repo>/commits/<sha>/statuses` answers, as far as the harness reads it. */
|
|
117
|
+
export const isCommitStatuses = arrayOf(
|
|
118
|
+
objectOf({ context: isString, state: isString }),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const isRegexOrNull = nullable(instanceOf(RegExp));
|
|
122
|
+
|
|
123
|
+
const isFormalCheck = objectOf(
|
|
124
|
+
{
|
|
125
|
+
name: isString,
|
|
126
|
+
covers: strings,
|
|
127
|
+
command: strings,
|
|
128
|
+
violation: isRegexOrNull,
|
|
129
|
+
passing: isRegexOrNull,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
files: strings,
|
|
133
|
+
baseCommand: strings,
|
|
134
|
+
behaviour: either(
|
|
135
|
+
objectOf({ kind: oneOf(["emit"]) }),
|
|
136
|
+
objectOf({ kind: oneOf(["probe"]), command: strings }),
|
|
137
|
+
),
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const isDoorRule = objectOf({
|
|
142
|
+
globs: strings,
|
|
143
|
+
funnels: strings,
|
|
144
|
+
containments: strings,
|
|
145
|
+
why: isString,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
/** The default export of a skill's `verify.config.ts`. `notUserFacing` may be absent. */
|
|
149
|
+
export const isSkillConfig = objectOf(
|
|
150
|
+
{
|
|
151
|
+
playground: isString,
|
|
152
|
+
prepare: arrayOf(strings),
|
|
153
|
+
userFacing: strings,
|
|
154
|
+
smoke: isString,
|
|
155
|
+
smokeJourney: isString,
|
|
156
|
+
doors: recordOf(nullable(isDoorRule)),
|
|
157
|
+
formal: arrayOf(isFormalCheck),
|
|
158
|
+
},
|
|
159
|
+
{ notUserFacing: strings, renderer: isString },
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @param {unknown} exported The default export of `verify.config.ts`
|
|
164
|
+
* @param {string} source The config file
|
|
165
|
+
* @returns {import("./skill.mjs").SkillConfig} The config
|
|
166
|
+
* @throws When the export is not a skill config
|
|
167
|
+
*/
|
|
168
|
+
export function decodeSkillConfig(exported, source) {
|
|
169
|
+
const config = decode(
|
|
170
|
+
isSkillConfig,
|
|
171
|
+
exported,
|
|
172
|
+
source,
|
|
173
|
+
"a verification skill config",
|
|
174
|
+
);
|
|
175
|
+
return { ...config, notUserFacing: config.notUserFacing ?? [] };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {unknown} stored What `result.json` holds
|
|
180
|
+
* @param {string} source The file
|
|
181
|
+
* @returns {import("./types.mjs").FormalResult} The result
|
|
182
|
+
* @throws When the value is not a formal check result
|
|
183
|
+
*/
|
|
184
|
+
export function decodeFormalResult(stored, source) {
|
|
185
|
+
return decode(isFormalResult, stored, source, "a formal check result");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {unknown} stored What `judgement.json` holds
|
|
190
|
+
* @param {string} source The file
|
|
191
|
+
* @returns {import("./types.mjs").RawJudgement} The judgement
|
|
192
|
+
* @throws When the value is not a judgement
|
|
193
|
+
*/
|
|
194
|
+
export function decodeRawJudgement(stored, source) {
|
|
195
|
+
return decode(isRawJudgement, stored, source, "a verifier's judgement");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {unknown} answer What `gh api` printed, parsed
|
|
200
|
+
* @param {string} source The command
|
|
201
|
+
* @returns {{ context: string, state: string }[]} The statuses
|
|
202
|
+
* @throws When the value is not a list of statuses
|
|
203
|
+
*/
|
|
204
|
+
export function decodeCommitStatuses(answer, source) {
|
|
205
|
+
return decode(isCommitStatuses, answer, source, "a list of commit statuses");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The journey results a run recorded for a feature.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} file The feature's `feature.json`
|
|
212
|
+
* @returns {import("./types.mjs").FeatureResult[]} The results, none when the journeys did not run
|
|
213
|
+
* @throws When the file is not a list of journey results
|
|
214
|
+
*/
|
|
215
|
+
export function readFeatureResults(file) {
|
|
216
|
+
if (!existsSync(file)) return [];
|
|
217
|
+
/** @type {unknown} */
|
|
218
|
+
const stored = JSON.parse(readFileSync(file, "utf8"));
|
|
219
|
+
return decode(isFeatureResults, stored, file, "a list of journey results");
|
|
220
|
+
}
|