@wildo-ai/saas-backend-lib 1.1.12 → 1.1.13
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/dist/esm/base-classes/models/manual-controllers.backend.definition.d.ts.map +1 -1
- package/dist/esm/base-classes/models/manual-controllers.backend.definition.js +2 -0
- package/dist/esm/base-classes/models/manual-controllers.backend.definition.js.map +1 -1
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts +4 -0
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js +20 -5
- package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts +3 -0
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js +10 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts +22 -0
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js +25 -1
- package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts +38 -3
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js +68 -5
- package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js.map +1 -1
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts +2 -2
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js +6 -3
- package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts +83 -0
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +217 -14
- package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts +32 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js +36 -0
- package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts +38 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js +83 -0
- package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts +36 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js +51 -0
- package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts +46 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js +68 -0
- package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts +27 -0
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js +42 -3
- package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts +26 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js +10 -0
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts +10 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js +60 -28
- package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js.map +1 -1
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts +90 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts.map +1 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js +373 -0
- package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js.map +1 -0
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts +29 -2
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts.map +1 -1
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.js +40 -3
- package/dist/esm/compliance/approval-seals/decision-presentation.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js +34 -6
- package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts +30 -6
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js +67 -22
- package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js +18 -1
- package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts +66 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js +161 -14
- package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts +47 -0
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts.map +1 -1
- package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.js.map +1 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.d.ts +27 -2
- package/dist/esm/external-providers/services/content-safety.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.js +46 -1
- package/dist/esm/external-providers/services/content-safety.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/embeddings.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/embeddings.backend.service.js +2 -0
- package/dist/esm/external-providers/services/embeddings.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +11 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +23 -0
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts +19 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts.map +1 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.js +41 -1
- package/dist/esm/external-providers/services/provider-service-account-token.backend.js.map +1 -1
- package/dist/esm/files/utils/file-parent-read-context.backend.d.ts +27 -2
- package/dist/esm/files/utils/file-parent-read-context.backend.d.ts.map +1 -1
- package/dist/esm/files/utils/file-parent-read-context.backend.js +23 -4
- package/dist/esm/files/utils/file-parent-read-context.backend.js.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts +26 -0
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js +100 -8
- package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts +3 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts.map +1 -1
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js +8 -2
- package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js.map +1 -1
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts +43 -0
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts.map +1 -1
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.js +37 -0
- package/dist/esm/flows-actors/models/function-tools.backend.definitions.js.map +1 -1
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts +7 -0
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js +8 -3
- package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +31 -6
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +78 -30
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js +4 -0
- package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts +28 -2
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.js +132 -22
- package/dist/esm/flows-actors/services/conversational-execution.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js +12 -8
- package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts +5 -0
- package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/flows-actors-registry.service.js +18 -2
- package/dist/esm/flows-actors/services/flows-actors-registry.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +33 -0
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +58 -0
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts +12 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js +23 -4
- package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts +12 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js +55 -17
- package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts +22 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js +52 -9
- package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts +79 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts.map +1 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js +211 -0
- package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js.map +1 -0
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts +2 -2
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts.map +1 -1
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js +9 -1
- package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/inject/models/container.d.ts.map +1 -1
- package/dist/esm/inject/models/container.js +6 -0
- package/dist/esm/inject/models/container.js.map +1 -1
- package/dist/esm/inject/models/service-types.d.ts +5 -0
- package/dist/esm/inject/models/service-types.d.ts.map +1 -1
- package/dist/esm/inject/models/service-types.js +3 -0
- package/dist/esm/inject/models/service-types.js.map +1 -1
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts +5 -0
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts.map +1 -1
- package/dist/esm/monitoring/observability/models/observability.backend.schemas.js.map +1 -1
- package/dist/esm/monitoring/observability/services/metrics.service.d.ts +34 -1
- package/dist/esm/monitoring/observability/services/metrics.service.d.ts.map +1 -1
- package/dist/esm/monitoring/observability/services/metrics.service.js +49 -0
- package/dist/esm/monitoring/observability/services/metrics.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts +6 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js +15 -4
- package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js +3 -1
- package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/http-server.backend.service.d.ts +3 -1
- package/dist/esm/runtime/services/http-server.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/http-server.backend.service.js +11 -3
- package/dist/esm/runtime/services/http-server.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts +22 -0
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts.map +1 -1
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js +54 -0
- package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts +51 -0
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js +87 -15
- package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js +5 -2
- package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js +65 -24
- package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts +64 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts.map +1 -1
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js +84 -2
- package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts +14 -0
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js +13 -5
- package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js +6 -2
- package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts +7 -0
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js +65 -20
- package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js +11 -4
- package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js.map +1 -1
- package/dist/esm/users/services/user-self-custom-implementations.backend.d.ts.map +1 -1
- package/dist/esm/users/services/user-self-custom-implementations.backend.js +4 -2
- package/dist/esm/users/services/user-self-custom-implementations.backend.js.map +1 -1
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.d.ts.map +1 -1
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js +16 -0
- package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Is an approval still current over its record? (#2914)
|
|
3
|
+
*
|
|
4
|
+
* A seal says what a person approved when they approved it. Whether that still describes the record is a different
|
|
5
|
+
* question, asked later: a month signed off, then a figure changed through a reopen or a write outside the operations.
|
|
6
|
+
* The seal cannot answer it alone, and the record cannot either; this file compares them.
|
|
7
|
+
*
|
|
8
|
+
* What is compared is the SEALED FIELD SET the approval's presentation carries (`subjectRecord`): the fields the tool
|
|
9
|
+
* declares the approval covers (`FunctionTool.sealedRecordFields`), shown to the person and committed to by the seal.
|
|
10
|
+
* The comparison itself is `approvalSealSubjectCurrencyOf` (`@wildo-ai/saas-models`), pure, so the operator door here
|
|
11
|
+
* and the offline verifier (`wildo seal verify --subject-record`) give one answer.
|
|
12
|
+
*
|
|
13
|
+
* Read-only, and it reads no person: the answer names the record, the standing and which sealed fields moved, never a
|
|
14
|
+
* value. So it is not recorded on the trail, unlike the bundle that hands out what the person was shown.
|
|
15
|
+
*/
|
|
16
|
+
import { type ApprovalSeal, type ApprovalSealSubjectCurrencyAssessment } from '@wildo-ai/saas-models';
|
|
17
|
+
/** What a currency check reads: the record the approval is about, as the system, and null when it is gone. */
|
|
18
|
+
export interface ApprovalSealCurrencyReads {
|
|
19
|
+
readRecord(resourceType: string, resourceId: string): Promise<Readonly<Record<string, unknown>> | null>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The currency of one seal's approval over its record: CURRENT, NO_LONGER_CURRENT, SUBJECT_GONE, or NO_SEALED_FIELD_SET
|
|
23
|
+
* when its presentation carries no record values (the tool declares none, or the presentation is not available).
|
|
24
|
+
*
|
|
25
|
+
* `presentationContent` is the seal's presentation as its row holds it, or as read back from its attested file (the
|
|
26
|
+
* caller reads that file, as the bundle does). A record of another organization than the seal's reads as gone: a seal
|
|
27
|
+
* answers for its own tenant's record only.
|
|
28
|
+
*
|
|
29
|
+
* assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — whether a person's approval still describes the record it is about is computed from the values they approved and the record as it is, never assumed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function assessApprovalSealCurrency(seal: Pick<ApprovalSeal, 'organizationId'>, presentationContent: unknown, reads: ApprovalSealCurrencyReads): Promise<ApprovalSealSubjectCurrencyAssessment>;
|
|
32
|
+
//# sourceMappingURL=approval-seal-currency.backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-currency.backend.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-currency.backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,qCAAqC,EAC3C,MAAM,uBAAuB,CAAC;AAE/B,8GAA8G;AAC9G,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACzG;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,gBAAgB,CAAC,EAC1C,mBAAmB,EAAE,OAAO,EAC5B,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,qCAAqC,CAAC,CAOhD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Is an approval still current over its record? (#2914)
|
|
3
|
+
*
|
|
4
|
+
* A seal says what a person approved when they approved it. Whether that still describes the record is a different
|
|
5
|
+
* question, asked later: a month signed off, then a figure changed through a reopen or a write outside the operations.
|
|
6
|
+
* The seal cannot answer it alone, and the record cannot either; this file compares them.
|
|
7
|
+
*
|
|
8
|
+
* What is compared is the SEALED FIELD SET the approval's presentation carries (`subjectRecord`): the fields the tool
|
|
9
|
+
* declares the approval covers (`FunctionTool.sealedRecordFields`), shown to the person and committed to by the seal.
|
|
10
|
+
* The comparison itself is `approvalSealSubjectCurrencyOf` (`@wildo-ai/saas-models`), pure, so the operator door here
|
|
11
|
+
* and the offline verifier (`wildo seal verify --subject-record`) give one answer.
|
|
12
|
+
*
|
|
13
|
+
* Read-only, and it reads no person: the answer names the record, the standing and which sealed fields moved, never a
|
|
14
|
+
* value. So it is not recorded on the trail, unlike the bundle that hands out what the person was shown.
|
|
15
|
+
*/
|
|
16
|
+
import { ApprovalSealSubjectCurrency, approvalSealSubjectCurrencyOf, humanDecisionPresentedRecordOf, } from '@wildo-ai/saas-models';
|
|
17
|
+
/**
|
|
18
|
+
* The currency of one seal's approval over its record: CURRENT, NO_LONGER_CURRENT, SUBJECT_GONE, or NO_SEALED_FIELD_SET
|
|
19
|
+
* when its presentation carries no record values (the tool declares none, or the presentation is not available).
|
|
20
|
+
*
|
|
21
|
+
* `presentationContent` is the seal's presentation as its row holds it, or as read back from its attested file (the
|
|
22
|
+
* caller reads that file, as the bundle does). A record of another organization than the seal's reads as gone: a seal
|
|
23
|
+
* answers for its own tenant's record only.
|
|
24
|
+
*
|
|
25
|
+
* assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — whether a person's approval still describes the record it is about is computed from the values they approved and the record as it is, never assumed.
|
|
26
|
+
*/
|
|
27
|
+
export async function assessApprovalSealCurrency(seal, presentationContent, reads) {
|
|
28
|
+
const sealed = humanDecisionPresentedRecordOf(presentationContent);
|
|
29
|
+
if (!sealed)
|
|
30
|
+
return { standing: ApprovalSealSubjectCurrency.NO_SEALED_FIELD_SET };
|
|
31
|
+
const record = await reads.readRecord(sealed.resourceType, sealed.resourceId);
|
|
32
|
+
const recordOrganizationId = record?.organizationId !== undefined && record?.organizationId !== null ? String(record.organizationId) : undefined;
|
|
33
|
+
const sameTenant = recordOrganizationId === undefined || recordOrganizationId === (seal.organizationId ?? undefined);
|
|
34
|
+
return approvalSealSubjectCurrencyOf(presentationContent, record && sameTenant ? record : null);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=approval-seal-currency.backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-currency.backend.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-currency.backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,EAC7B,8BAA8B,GAG/B,MAAM,uBAAuB,CAAC;AAO/B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAA0C,EAC1C,mBAA4B,EAC5B,KAAgC;IAEhC,MAAM,MAAM,GAAG,8BAA8B,CAAC,mBAAmB,CAAC,CAAC;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,2BAA2B,CAAC,mBAAmB,EAAE,CAAC;IAClF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9E,MAAM,oBAAoB,GAAG,MAAM,EAAE,cAAc,KAAK,SAAS,IAAI,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjJ,MAAM,UAAU,GAAG,oBAAoB,KAAK,SAAS,IAAI,oBAAoB,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,SAAS,CAAC,CAAC;IACrH,OAAO,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAClG,CAAC","sourcesContent":["/**\n * # Is an approval still current over its record? (#2914)\n *\n * A seal says what a person approved when they approved it. Whether that still describes the record is a different\n * question, asked later: a month signed off, then a figure changed through a reopen or a write outside the operations.\n * The seal cannot answer it alone, and the record cannot either; this file compares them.\n *\n * What is compared is the SEALED FIELD SET the approval's presentation carries (`subjectRecord`): the fields the tool\n * declares the approval covers (`FunctionTool.sealedRecordFields`), shown to the person and committed to by the seal.\n * The comparison itself is `approvalSealSubjectCurrencyOf` (`@wildo-ai/saas-models`), pure, so the operator door here\n * and the offline verifier (`wildo seal verify --subject-record`) give one answer.\n *\n * Read-only, and it reads no person: the answer names the record, the standing and which sealed fields moved, never a\n * value. So it is not recorded on the trail, unlike the bundle that hands out what the person was shown.\n */\nimport {\n ApprovalSealSubjectCurrency,\n approvalSealSubjectCurrencyOf,\n humanDecisionPresentedRecordOf,\n type ApprovalSeal,\n type ApprovalSealSubjectCurrencyAssessment,\n} from '@wildo-ai/saas-models';\n\n/** What a currency check reads: the record the approval is about, as the system, and null when it is gone. */\nexport interface ApprovalSealCurrencyReads {\n readRecord(resourceType: string, resourceId: string): Promise<Readonly<Record<string, unknown>> | null>;\n}\n\n/**\n * The currency of one seal's approval over its record: CURRENT, NO_LONGER_CURRENT, SUBJECT_GONE, or NO_SEALED_FIELD_SET\n * when its presentation carries no record values (the tool declares none, or the presentation is not available).\n *\n * `presentationContent` is the seal's presentation as its row holds it, or as read back from its attested file (the\n * caller reads that file, as the bundle does). A record of another organization than the seal's reads as gone: a seal\n * answers for its own tenant's record only.\n *\n * assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — whether a person's approval still describes the record it is about is computed from the values they approved and the record as it is, never assumed.\n */\nexport async function assessApprovalSealCurrency(\n seal: Pick<ApprovalSeal, 'organizationId'>,\n presentationContent: unknown,\n reads: ApprovalSealCurrencyReads,\n): Promise<ApprovalSealSubjectCurrencyAssessment> {\n const sealed = humanDecisionPresentedRecordOf(presentationContent);\n if (!sealed) return { standing: ApprovalSealSubjectCurrency.NO_SEALED_FIELD_SET };\n const record = await reads.readRecord(sealed.resourceType, sealed.resourceId);\n const recordOrganizationId = record?.organizationId !== undefined && record?.organizationId !== null ? String(record.organizationId) : undefined;\n const sameTenant = recordOrganizationId === undefined || recordOrganizationId === (seal.organizationId ?? undefined);\n return approvalSealSubjectCurrencyOf(presentationContent, record && sameTenant ? record : null);\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # An approval seal, as the record it approved shows it (#2913)
|
|
3
|
+
*
|
|
4
|
+
* The projection behind `GET /approval-seals/:sealId/records/:resourceType/:resourceId/:field`
|
|
5
|
+
* (`ApprovalSealsOnRecordControllerBackendService`). Pure: the controller reads the seal, its successor, the record's
|
|
6
|
+
* currency and its manifested copy's standing, and this function decides what a reader of the record is told. The
|
|
7
|
+
* contract, and why each member is safe to hand that reader, is `approval-seal-on-record.shared.schemas.ts`.
|
|
8
|
+
*/
|
|
9
|
+
import { ApprovalSealOnRecordState, type ApprovalSeal, type ApprovalSealOnRecordView, type ApprovalSealSubjectCurrencyAssessment } from '@wildo-ai/saas-models';
|
|
10
|
+
/** What the controller found, for {@link approvalSealOnRecordViewOf}. */
|
|
11
|
+
export interface ApprovalSealOnRecordFacts {
|
|
12
|
+
readonly sealId: string;
|
|
13
|
+
/** The live seal, or null when it is not live (retained after its approver's erasure, or gone). */
|
|
14
|
+
readonly seal: ApprovalSeal | null;
|
|
15
|
+
/** The seal exists, retained after its approver's erasure: nothing of it is shown but that. */
|
|
16
|
+
readonly retained: boolean;
|
|
17
|
+
/** A later decision's seal that replaced this one. */
|
|
18
|
+
readonly successor: {
|
|
19
|
+
readonly sealId?: string;
|
|
20
|
+
readonly retained?: true;
|
|
21
|
+
} | null;
|
|
22
|
+
/** The approval's currency over the record (#2914), when it was asked. */
|
|
23
|
+
readonly currency?: ApprovalSealSubjectCurrencyAssessment;
|
|
24
|
+
/** Whether the seal's manifested copy is a file this reader can download now. */
|
|
25
|
+
readonly manifestedCopyServable: boolean;
|
|
26
|
+
/** The public check's address without its query (`approvalSealPublicCheckUrlOf`); undefined with no public API URL. */
|
|
27
|
+
readonly publicCheckUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
/** The approver's printed name, from the approver's disclosure on the row. Undefined once erased, or when none was given. */
|
|
30
|
+
export declare function approvalSealApproverNameOf(seal: Pick<ApprovalSeal, 'disclosures'>): string | undefined;
|
|
31
|
+
/** The state a reader of the record sees. A superseded seal is SUPERSEDED whatever its signing stood at. */
|
|
32
|
+
export declare function approvalSealOnRecordStateOf(facts: Pick<ApprovalSealOnRecordFacts, 'seal' | 'retained' | 'successor'>): ApprovalSealOnRecordState;
|
|
33
|
+
/**
|
|
34
|
+
* What a reader of the record is told about the seal it stores. Erased approvers keep their seal and lose their name: an
|
|
35
|
+
* APPROVER_ERASED view carries the seal id and nothing of the row. Currency names the fields that moved, never a value.
|
|
36
|
+
*/
|
|
37
|
+
export declare function approvalSealOnRecordViewOf(facts: ApprovalSealOnRecordFacts): ApprovalSealOnRecordView;
|
|
38
|
+
//# sourceMappingURL=approval-seal-on-record.backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-on-record.backend.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-on-record.backend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,yBAAyB,EAIzB,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,KAAK,qCAAqC,EAC3C,MAAM,uBAAuB,CAAC;AAG/B,yEAAyE;AACzE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mGAAmG;IACnG,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAClF,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,qCAAqC,CAAC;IAC1D,iFAAiF;IACjF,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,uHAAuH;IACvH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,6HAA6H;AAC7H,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,GAAG,MAAM,GAAG,SAAS,CAItG;AAED,4GAA4G;AAC5G,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC,GAAG,yBAAyB,CAkBhJ;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,yBAAyB,GAAG,wBAAwB,CAqCrG"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # An approval seal, as the record it approved shows it (#2913)
|
|
3
|
+
*
|
|
4
|
+
* The projection behind `GET /approval-seals/:sealId/records/:resourceType/:resourceId/:field`
|
|
5
|
+
* (`ApprovalSealsOnRecordControllerBackendService`). Pure: the controller reads the seal, its successor, the record's
|
|
6
|
+
* currency and its manifested copy's standing, and this function decides what a reader of the record is told. The
|
|
7
|
+
* contract, and why each member is safe to hand that reader, is `approval-seal-on-record.shared.schemas.ts`.
|
|
8
|
+
*/
|
|
9
|
+
import { ApprovalSealOnRecordState, ApprovalSealStatus, ApprovalSealSubjectCurrency, HumanDecision_PresentedFieldCurrency, } from '@wildo-ai/saas-models';
|
|
10
|
+
import { ApprovalSealDisclosureClaim } from '@wildo-ai/platform-config-lib';
|
|
11
|
+
/** The approver's printed name, from the approver's disclosure on the row. Undefined once erased, or when none was given. */
|
|
12
|
+
export function approvalSealApproverNameOf(seal) {
|
|
13
|
+
const approver = (seal.disclosures ?? []).find((disclosure) => disclosure.claimName === ApprovalSealDisclosureClaim.APPROVER);
|
|
14
|
+
const name = approver?.value?.name;
|
|
15
|
+
return typeof name === 'string' && name.trim().length > 0 ? name : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** The state a reader of the record sees. A superseded seal is SUPERSEDED whatever its signing stood at. */
|
|
18
|
+
export function approvalSealOnRecordStateOf(facts) {
|
|
19
|
+
if (facts.retained)
|
|
20
|
+
return ApprovalSealOnRecordState.APPROVER_ERASED;
|
|
21
|
+
const seal = facts.seal;
|
|
22
|
+
if (!seal)
|
|
23
|
+
return ApprovalSealOnRecordState.NO_SEAL;
|
|
24
|
+
if (facts.successor)
|
|
25
|
+
return ApprovalSealOnRecordState.SUPERSEDED;
|
|
26
|
+
// A live row whose approver disclosure is gone was scrubbed by an erasure: the name is gone, the decision is not.
|
|
27
|
+
if (!(seal.disclosures ?? []).some((disclosure) => disclosure.claimName === ApprovalSealDisclosureClaim.APPROVER)) {
|
|
28
|
+
return ApprovalSealOnRecordState.APPROVER_ERASED;
|
|
29
|
+
}
|
|
30
|
+
switch (seal.status) {
|
|
31
|
+
case ApprovalSealStatus.SEALED:
|
|
32
|
+
return ApprovalSealOnRecordState.SEALED;
|
|
33
|
+
case ApprovalSealStatus.DEAD:
|
|
34
|
+
return ApprovalSealOnRecordState.SEAL_FAILED;
|
|
35
|
+
case ApprovalSealStatus.PENDING:
|
|
36
|
+
case ApprovalSealStatus.IN_FLIGHT:
|
|
37
|
+
return ApprovalSealOnRecordState.PENDING;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What a reader of the record is told about the seal it stores. Erased approvers keep their seal and lose their name: an
|
|
42
|
+
* APPROVER_ERASED view carries the seal id and nothing of the row. Currency names the fields that moved, never a value.
|
|
43
|
+
*/
|
|
44
|
+
export function approvalSealOnRecordViewOf(facts) {
|
|
45
|
+
const state = approvalSealOnRecordStateOf(facts);
|
|
46
|
+
const seal = facts.seal;
|
|
47
|
+
if (!seal || facts.retained)
|
|
48
|
+
return { sealId: facts.sealId, state };
|
|
49
|
+
const approverName = approvalSealApproverNameOf(seal);
|
|
50
|
+
const assessment = facts.currency;
|
|
51
|
+
const currency = assessment && assessment.standing !== ApprovalSealSubjectCurrency.NO_SEALED_FIELD_SET
|
|
52
|
+
? {
|
|
53
|
+
standing: assessment.standing,
|
|
54
|
+
movedFields: (assessment.fields ?? []).filter((field) => field.currency !== HumanDecision_PresentedFieldCurrency.CURRENT).map((field) => field.key),
|
|
55
|
+
}
|
|
56
|
+
: undefined;
|
|
57
|
+
const copy = seal.manifestedCopy;
|
|
58
|
+
const copyNamed = copy !== null && typeof copy === 'object' && typeof copy.fileId === 'string';
|
|
59
|
+
const signed = seal.status === ApprovalSealStatus.SEALED && typeof seal.jws === 'string' && seal.jws.length > 0;
|
|
60
|
+
return {
|
|
61
|
+
sealId: facts.sealId,
|
|
62
|
+
state,
|
|
63
|
+
...(approverName ? { approverName } : {}),
|
|
64
|
+
decidedAt: new Date(seal.decidedAt).toISOString(),
|
|
65
|
+
...(signed && seal.sealedAt ? { sealedAt: new Date(seal.sealedAt).toISOString() } : {}),
|
|
66
|
+
meaning: seal.meaning,
|
|
67
|
+
outcome: seal.outcome,
|
|
68
|
+
...(facts.successor?.sealId ? { supersededBySealId: facts.successor.sealId } : {}),
|
|
69
|
+
...(currency ? { currency } : {}),
|
|
70
|
+
...(seal.deliverableFileId
|
|
71
|
+
? {
|
|
72
|
+
deliverable: {
|
|
73
|
+
fileId: seal.deliverableFileId,
|
|
74
|
+
manifestedCopyAvailable: copyNamed && facts.manifestedCopyServable,
|
|
75
|
+
manifestedCopyErased: !copyNamed && seal.manifestedCopyAt !== undefined,
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: {}),
|
|
79
|
+
...(signed ? { jws: seal.jws } : {}),
|
|
80
|
+
...(signed && facts.publicCheckUrl ? { verificationUrl: `${facts.publicCheckUrl}?jws=${encodeURIComponent(seal.jws)}` } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=approval-seal-on-record.backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-on-record.backend.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-on-record.backend.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * # An approval seal, as the record it approved shows it (#2913)\n *\n * The projection behind `GET /approval-seals/:sealId/records/:resourceType/:resourceId/:field`\n * (`ApprovalSealsOnRecordControllerBackendService`). Pure: the controller reads the seal, its successor, the record's\n * currency and its manifested copy's standing, and this function decides what a reader of the record is told. The\n * contract, and why each member is safe to hand that reader, is `approval-seal-on-record.shared.schemas.ts`.\n */\nimport {\n ApprovalSealOnRecordState,\n ApprovalSealStatus,\n ApprovalSealSubjectCurrency,\n HumanDecision_PresentedFieldCurrency,\n type ApprovalSeal,\n type ApprovalSealOnRecordView,\n type ApprovalSealSubjectCurrencyAssessment,\n} from '@wildo-ai/saas-models';\nimport { ApprovalSealDisclosureClaim } from '@wildo-ai/platform-config-lib';\n\n/** What the controller found, for {@link approvalSealOnRecordViewOf}. */\nexport interface ApprovalSealOnRecordFacts {\n readonly sealId: string;\n /** The live seal, or null when it is not live (retained after its approver's erasure, or gone). */\n readonly seal: ApprovalSeal | null;\n /** The seal exists, retained after its approver's erasure: nothing of it is shown but that. */\n readonly retained: boolean;\n /** A later decision's seal that replaced this one. */\n readonly successor: { readonly sealId?: string; readonly retained?: true } | null;\n /** The approval's currency over the record (#2914), when it was asked. */\n readonly currency?: ApprovalSealSubjectCurrencyAssessment;\n /** Whether the seal's manifested copy is a file this reader can download now. */\n readonly manifestedCopyServable: boolean;\n /** The public check's address without its query (`approvalSealPublicCheckUrlOf`); undefined with no public API URL. */\n readonly publicCheckUrl?: string;\n}\n\n/** The approver's printed name, from the approver's disclosure on the row. Undefined once erased, or when none was given. */\nexport function approvalSealApproverNameOf(seal: Pick<ApprovalSeal, 'disclosures'>): string | undefined {\n const approver = (seal.disclosures ?? []).find((disclosure) => disclosure.claimName === ApprovalSealDisclosureClaim.APPROVER);\n const name = approver?.value?.name;\n return typeof name === 'string' && name.trim().length > 0 ? name : undefined;\n}\n\n/** The state a reader of the record sees. A superseded seal is SUPERSEDED whatever its signing stood at. */\nexport function approvalSealOnRecordStateOf(facts: Pick<ApprovalSealOnRecordFacts, 'seal' | 'retained' | 'successor'>): ApprovalSealOnRecordState {\n if (facts.retained) return ApprovalSealOnRecordState.APPROVER_ERASED;\n const seal = facts.seal;\n if (!seal) return ApprovalSealOnRecordState.NO_SEAL;\n if (facts.successor) return ApprovalSealOnRecordState.SUPERSEDED;\n // A live row whose approver disclosure is gone was scrubbed by an erasure: the name is gone, the decision is not.\n if (!(seal.disclosures ?? []).some((disclosure) => disclosure.claimName === ApprovalSealDisclosureClaim.APPROVER)) {\n return ApprovalSealOnRecordState.APPROVER_ERASED;\n }\n switch (seal.status) {\n case ApprovalSealStatus.SEALED:\n return ApprovalSealOnRecordState.SEALED;\n case ApprovalSealStatus.DEAD:\n return ApprovalSealOnRecordState.SEAL_FAILED;\n case ApprovalSealStatus.PENDING:\n case ApprovalSealStatus.IN_FLIGHT:\n return ApprovalSealOnRecordState.PENDING;\n }\n}\n\n/**\n * What a reader of the record is told about the seal it stores. Erased approvers keep their seal and lose their name: an\n * APPROVER_ERASED view carries the seal id and nothing of the row. Currency names the fields that moved, never a value.\n */\nexport function approvalSealOnRecordViewOf(facts: ApprovalSealOnRecordFacts): ApprovalSealOnRecordView {\n const state = approvalSealOnRecordStateOf(facts);\n const seal = facts.seal;\n if (!seal || facts.retained) return { sealId: facts.sealId, state };\n const approverName = approvalSealApproverNameOf(seal);\n const assessment = facts.currency;\n const currency = assessment && assessment.standing !== ApprovalSealSubjectCurrency.NO_SEALED_FIELD_SET\n ? {\n standing: assessment.standing,\n movedFields: (assessment.fields ?? []).filter((field) => field.currency !== HumanDecision_PresentedFieldCurrency.CURRENT).map((field) => field.key),\n }\n : undefined;\n const copy = seal.manifestedCopy as unknown;\n const copyNamed = copy !== null && typeof copy === 'object' && typeof (copy as { fileId?: unknown }).fileId === 'string';\n const signed = seal.status === ApprovalSealStatus.SEALED && typeof seal.jws === 'string' && seal.jws.length > 0;\n return {\n sealId: facts.sealId,\n state,\n ...(approverName ? { approverName } : {}),\n decidedAt: new Date(seal.decidedAt).toISOString(),\n ...(signed && seal.sealedAt ? { sealedAt: new Date(seal.sealedAt).toISOString() } : {}),\n meaning: seal.meaning,\n outcome: seal.outcome,\n ...(facts.successor?.sealId ? { supersededBySealId: facts.successor.sealId } : {}),\n ...(currency ? { currency } : {}),\n ...(seal.deliverableFileId\n ? {\n deliverable: {\n fileId: seal.deliverableFileId,\n manifestedCopyAvailable: copyNamed && facts.manifestedCopyServable,\n manifestedCopyErased: !copyNamed && seal.manifestedCopyAt !== undefined,\n },\n }\n : {}),\n ...(signed ? { jws: seal.jws! } : {}),\n ...(signed && facts.publicCheckUrl ? { verificationUrl: `${facts.publicCheckUrl}?jws=${encodeURIComponent(seal.jws!)}` } : {}),\n };\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ApprovalSeal } from '@wildo-ai/saas-models';
|
|
2
|
+
import type { FilesBackendService } from '../../files/services/files.backend.service';
|
|
3
|
+
import type { ExecutionContextCreatorBackendService } from '../../runtime/services/execution-context-creator.backend.service';
|
|
4
|
+
/** What reading a seal's presentation found. */
|
|
5
|
+
export declare enum ApprovalSealPresentationReadOutcome {
|
|
6
|
+
/** The content, from the row or from a file whose bytes match the recorded hash. */
|
|
7
|
+
CONTENT = "content",
|
|
8
|
+
/** The seal holds no presentation (a plane that builds none, or one an erasure removed). */
|
|
9
|
+
NONE = "none",
|
|
10
|
+
/** The file's bytes are not the ones the row recorded. */
|
|
11
|
+
TAMPERED = "tampered",
|
|
12
|
+
/** The row names a file nothing can read: gone from the files store, no file service bound, or a failed read. */
|
|
13
|
+
UNREADABLE = "unreadable"
|
|
14
|
+
}
|
|
15
|
+
export type ApprovalSealPresentationRead = {
|
|
16
|
+
readonly outcome: ApprovalSealPresentationReadOutcome.CONTENT;
|
|
17
|
+
readonly content: Record<string, unknown>;
|
|
18
|
+
} | {
|
|
19
|
+
readonly outcome: ApprovalSealPresentationReadOutcome.NONE;
|
|
20
|
+
} | {
|
|
21
|
+
readonly outcome: ApprovalSealPresentationReadOutcome.TAMPERED;
|
|
22
|
+
readonly fileId: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly outcome: ApprovalSealPresentationReadOutcome.UNREADABLE;
|
|
25
|
+
readonly fileId: string;
|
|
26
|
+
readonly reason: string;
|
|
27
|
+
};
|
|
28
|
+
/** What the read needs: the `files` row a presentation names, its bytes, and the system context they are read in. */
|
|
29
|
+
export interface ApprovalSealPresentationReadDependencies {
|
|
30
|
+
readonly readFileRow: (fileId: string) => Promise<Record<string, unknown> | null>;
|
|
31
|
+
readonly files?: Pick<FilesBackendService, 'readFileBytes'>;
|
|
32
|
+
readonly contexts?: Pick<ExecutionContextCreatorBackendService, 'createForInternalSystemOperation'>;
|
|
33
|
+
}
|
|
34
|
+
/** Read one seal's presentation, from its row or its attested file, checking the file against the hash its row recorded. */
|
|
35
|
+
export declare function readApprovalSealPresentation(seal: Pick<ApprovalSeal, 'presentation'>, dependencies: ApprovalSealPresentationReadDependencies): Promise<ApprovalSealPresentationRead>;
|
|
36
|
+
//# sourceMappingURL=approval-seal-presentation-read.backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-presentation-read.backend.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-presentation-read.backend.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAQ,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,kEAAkE,CAAC;AAG9H,gDAAgD;AAChD,oBAAY,mCAAmC;IAC7C,oFAAoF;IACpF,OAAO,YAAY;IACnB,4FAA4F;IAC5F,IAAI,SAAS;IACb,0DAA0D;IAC1D,QAAQ,aAAa;IACrB,iHAAiH;IACjH,UAAU,eAAe;CAC1B;AAED,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,CAAC,OAAO,EAAE,mCAAmC,CAAC,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,OAAO,EAAE,mCAAmC,CAAC,IAAI,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,OAAO,EAAE,mCAAmC,CAAC,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3F;IAAE,QAAQ,CAAC,OAAO,EAAE,mCAAmC,CAAC,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3H,qHAAqH;AACrH,MAAM,WAAW,wCAAwC;IACvD,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,qCAAqC,EAAE,kCAAkC,CAAC,CAAC;CACrG;AAED,4HAA4H;AAC5H,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EACxC,YAAY,EAAE,wCAAwC,GACrD,OAAO,CAAC,4BAA4B,CAAC,CAoBvC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Reading a seal's presentation back (#2903 S6b, #2913)
|
|
3
|
+
*
|
|
4
|
+
* A seal's presentation is in its row, or, over the inline ceiling, in a frozen file the relay moved it to. Every reader
|
|
5
|
+
* that needs the content (the operator's bundle and currency doors, the record's view of its seal) reads it here, so a
|
|
6
|
+
* file whose bytes are not the ones the row recorded is refused the same way everywhere, never handed out as the
|
|
7
|
+
* presentation.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from 'crypto';
|
|
10
|
+
import { describeCaughtError } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
11
|
+
/** What reading a seal's presentation found. */
|
|
12
|
+
export var ApprovalSealPresentationReadOutcome;
|
|
13
|
+
(function (ApprovalSealPresentationReadOutcome) {
|
|
14
|
+
/** The content, from the row or from a file whose bytes match the recorded hash. */
|
|
15
|
+
ApprovalSealPresentationReadOutcome["CONTENT"] = "content";
|
|
16
|
+
/** The seal holds no presentation (a plane that builds none, or one an erasure removed). */
|
|
17
|
+
ApprovalSealPresentationReadOutcome["NONE"] = "none";
|
|
18
|
+
/** The file's bytes are not the ones the row recorded. */
|
|
19
|
+
ApprovalSealPresentationReadOutcome["TAMPERED"] = "tampered";
|
|
20
|
+
/** The row names a file nothing can read: gone from the files store, no file service bound, or a failed read. */
|
|
21
|
+
ApprovalSealPresentationReadOutcome["UNREADABLE"] = "unreadable";
|
|
22
|
+
})(ApprovalSealPresentationReadOutcome || (ApprovalSealPresentationReadOutcome = {}));
|
|
23
|
+
/** Read one seal's presentation, from its row or its attested file, checking the file against the hash its row recorded. */
|
|
24
|
+
export async function readApprovalSealPresentation(seal, dependencies) {
|
|
25
|
+
const presentation = seal.presentation;
|
|
26
|
+
if (!presentation)
|
|
27
|
+
return { outcome: ApprovalSealPresentationReadOutcome.NONE };
|
|
28
|
+
if (presentation.content)
|
|
29
|
+
return { outcome: ApprovalSealPresentationReadOutcome.CONTENT, content: presentation.content };
|
|
30
|
+
if (!presentation.file)
|
|
31
|
+
return { outcome: ApprovalSealPresentationReadOutcome.NONE };
|
|
32
|
+
const fileId = presentation.file.fileId;
|
|
33
|
+
const unreadable = (reason) => ({ outcome: ApprovalSealPresentationReadOutcome.UNREADABLE, fileId, reason });
|
|
34
|
+
if (!dependencies.files || !dependencies.contexts)
|
|
35
|
+
return unreadable('no file service is bound');
|
|
36
|
+
const fileRow = await dependencies.readFileRow(fileId);
|
|
37
|
+
if (!fileRow)
|
|
38
|
+
return unreadable('the file row is gone');
|
|
39
|
+
let bytes;
|
|
40
|
+
try {
|
|
41
|
+
bytes = await dependencies.files.readFileBytes(fileRow, await dependencies.contexts.createForInternalSystemOperation());
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return unreadable(describeCaughtError(error));
|
|
45
|
+
}
|
|
46
|
+
if (createHash('sha256').update(bytes).digest('hex') !== presentation.file.contentSha256) {
|
|
47
|
+
return { outcome: ApprovalSealPresentationReadOutcome.TAMPERED, fileId };
|
|
48
|
+
}
|
|
49
|
+
return { outcome: ApprovalSealPresentationReadOutcome.CONTENT, content: JSON.parse(bytes.toString('utf8')) };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=approval-seal-presentation-read.backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-seal-presentation-read.backend.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-presentation-read.backend.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAIpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,8EAA8E,CAAC;AAEnH,gDAAgD;AAChD,MAAM,CAAN,IAAY,mCASX;AATD,WAAY,mCAAmC;IAC7C,oFAAoF;IACpF,0DAAmB,CAAA;IACnB,4FAA4F;IAC5F,oDAAa,CAAA;IACb,0DAA0D;IAC1D,4DAAqB,CAAA;IACrB,iHAAiH;IACjH,gEAAyB,CAAA;AAC3B,CAAC,EATW,mCAAmC,KAAnC,mCAAmC,QAS9C;AAeD,4HAA4H;AAC5H,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,IAAwC,EACxC,YAAsD;IAEtD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,IAAI,CAAC,YAAY;QAAE,OAAO,EAAE,OAAO,EAAE,mCAAmC,CAAC,IAAI,EAAE,CAAC;IAChF,IAAI,YAAY,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,mCAAmC,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;IACzH,IAAI,CAAC,YAAY,CAAC,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,mCAAmC,CAAC,IAAI,EAAE,CAAC;IACrF,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,MAAc,EAAgC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,mCAAmC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnJ,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAC,0BAA0B,CAAC,CAAC;IACjG,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC,sBAAsB,CAAC,CAAC;IACxD,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,OAAe,EAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,gCAAgC,EAAE,CAAC,CAAC;IAClI,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QACzF,OAAO,EAAE,OAAO,EAAE,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,mCAAmC,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA4B,EAAE,CAAC;AAC1I,CAAC","sourcesContent":["/**\n * # Reading a seal's presentation back (#2903 S6b, #2913)\n *\n * A seal's presentation is in its row, or, over the inline ceiling, in a frozen file the relay moved it to. Every reader\n * that needs the content (the operator's bundle and currency doors, the record's view of its seal) reads it here, so a\n * file whose bytes are not the ones the row recorded is refused the same way everywhere, never handed out as the\n * presentation.\n */\nimport { createHash } from 'crypto';\nimport type { ApprovalSeal, File } from '@wildo-ai/saas-models';\nimport type { FilesBackendService } from '../../files/services/files.backend.service';\nimport type { ExecutionContextCreatorBackendService } from '../../runtime/services/execution-context-creator.backend.service';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\n/** What reading a seal's presentation found. */\nexport enum ApprovalSealPresentationReadOutcome {\n /** The content, from the row or from a file whose bytes match the recorded hash. */\n CONTENT = 'content',\n /** The seal holds no presentation (a plane that builds none, or one an erasure removed). */\n NONE = 'none',\n /** The file's bytes are not the ones the row recorded. */\n TAMPERED = 'tampered',\n /** The row names a file nothing can read: gone from the files store, no file service bound, or a failed read. */\n UNREADABLE = 'unreadable',\n}\n\nexport type ApprovalSealPresentationRead =\n | { readonly outcome: ApprovalSealPresentationReadOutcome.CONTENT; readonly content: Record<string, unknown> }\n | { readonly outcome: ApprovalSealPresentationReadOutcome.NONE }\n | { readonly outcome: ApprovalSealPresentationReadOutcome.TAMPERED; readonly fileId: string }\n | { readonly outcome: ApprovalSealPresentationReadOutcome.UNREADABLE; readonly fileId: string; readonly reason: string };\n\n/** What the read needs: the `files` row a presentation names, its bytes, and the system context they are read in. */\nexport interface ApprovalSealPresentationReadDependencies {\n readonly readFileRow: (fileId: string) => Promise<Record<string, unknown> | null>;\n readonly files?: Pick<FilesBackendService, 'readFileBytes'>;\n readonly contexts?: Pick<ExecutionContextCreatorBackendService, 'createForInternalSystemOperation'>;\n}\n\n/** Read one seal's presentation, from its row or its attested file, checking the file against the hash its row recorded. */\nexport async function readApprovalSealPresentation(\n seal: Pick<ApprovalSeal, 'presentation'>,\n dependencies: ApprovalSealPresentationReadDependencies,\n): Promise<ApprovalSealPresentationRead> {\n const presentation = seal.presentation;\n if (!presentation) return { outcome: ApprovalSealPresentationReadOutcome.NONE };\n if (presentation.content) return { outcome: ApprovalSealPresentationReadOutcome.CONTENT, content: presentation.content };\n if (!presentation.file) return { outcome: ApprovalSealPresentationReadOutcome.NONE };\n const fileId = presentation.file.fileId;\n const unreadable = (reason: string): ApprovalSealPresentationRead => ({ outcome: ApprovalSealPresentationReadOutcome.UNREADABLE, fileId, reason });\n if (!dependencies.files || !dependencies.contexts) return unreadable('no file service is bound');\n const fileRow = await dependencies.readFileRow(fileId);\n if (!fileRow) return unreadable('the file row is gone');\n let bytes: Buffer;\n try {\n bytes = await dependencies.files.readFileBytes(fileRow as File, await dependencies.contexts.createForInternalSystemOperation());\n } catch (error) {\n return unreadable(describeCaughtError(error));\n }\n if (createHash('sha256').update(bytes).digest('hex') !== presentation.file.contentSha256) {\n return { outcome: ApprovalSealPresentationReadOutcome.TAMPERED, fileId };\n }\n return { outcome: ApprovalSealPresentationReadOutcome.CONTENT, content: JSON.parse(bytes.toString('utf8')) as Record<string, unknown> };\n}\n"]}
|
|
@@ -100,6 +100,8 @@ export declare function signedFactsOfJws(jws: string): {
|
|
|
100
100
|
readonly claimsDigest?: string;
|
|
101
101
|
};
|
|
102
102
|
/** See the file's header. */
|
|
103
|
+
/** How many seals of one file the file's view reads to find the one its record stores (#2913). */
|
|
104
|
+
export declare const ATTESTING_SEAL_SCAN_LIMIT = 10;
|
|
103
105
|
export declare class ApprovalSealRelayStore {
|
|
104
106
|
private readonly repositoriesRegistry;
|
|
105
107
|
private readonly clock;
|
|
@@ -113,6 +115,50 @@ export declare class ApprovalSealRelayStore {
|
|
|
113
115
|
* finish a seal's signing and hand no field of it to anyone; an export reads through {@link read} and the reveal.
|
|
114
116
|
*/
|
|
115
117
|
readIncludingRetained(sealId: string): Promise<ApprovalSealRelayRow | null>;
|
|
118
|
+
/**
|
|
119
|
+
* The record a seal its approver's erasure RETAINED is about, and nothing else of it (#2913): what a record's view of
|
|
120
|
+
* its seal needs to say "approver erased" only to a reader of that very record. No field of the retained row but its
|
|
121
|
+
* subject leaves this method, which is what keeps the record's view outside the reasons the completion door is
|
|
122
|
+
* unaudited (`retained-completion-door-callers.guard.test.ts`).
|
|
123
|
+
*/
|
|
124
|
+
retainedSubjectOf(sealId: string): Promise<{
|
|
125
|
+
readonly resourceType: string;
|
|
126
|
+
readonly resourceId: string;
|
|
127
|
+
} | null>;
|
|
128
|
+
/**
|
|
129
|
+
* The seal that superseded one (#2913): a later decision on the same record whose seal names it in `supersedesSealId`.
|
|
130
|
+
* The id of a live successor; `retained: true` without an id for one its own approver's erasure retained, so nothing of
|
|
131
|
+
* a retained row leaves but the fact that it exists. Null when nothing superseded it. Asked within the seal's own record
|
|
132
|
+
* (the indexed subject fields): what a record shows is whether IT was approved again, and a successor about another
|
|
133
|
+
* record (a reopened decision whose answer named another row) is not that record's supersession.
|
|
134
|
+
*/
|
|
135
|
+
successorOf(sealId: string, subject: {
|
|
136
|
+
readonly resourceType: string;
|
|
137
|
+
readonly resourceId: string;
|
|
138
|
+
}): Promise<{
|
|
139
|
+
readonly sealId?: string;
|
|
140
|
+
readonly retained?: true;
|
|
141
|
+
} | null>;
|
|
142
|
+
/**
|
|
143
|
+
* The live seals that attest a file as their deliverable (#2913, `deliverableFileId`, indexed), latest decision first:
|
|
144
|
+
* what a file's view of its seal chooses from. Bounded: a file is attested by a handful of decisions at most.
|
|
145
|
+
*/
|
|
146
|
+
attestingFile(fileId: string, limit?: number): Promise<ApprovalSeal[]>;
|
|
147
|
+
/**
|
|
148
|
+
* The seals its approver's erasure RETAINED that attest a file, as their ids and subjects only (#2913): enough for a
|
|
149
|
+
* file's view to say "approver erased" to a reader of the file's record, after checking the seal's own record stores it.
|
|
150
|
+
* No other field of a retained row leaves this method.
|
|
151
|
+
*/
|
|
152
|
+
retainedAttestingFile(fileId: string, limit?: number): Promise<Array<{
|
|
153
|
+
readonly sealId: string;
|
|
154
|
+
readonly resourceType: string;
|
|
155
|
+
readonly resourceId: string;
|
|
156
|
+
}>>;
|
|
157
|
+
/**
|
|
158
|
+
* The latest live seal of one decision whose approver is `approverUserId` (#2913): the decider's own view of the seal of
|
|
159
|
+
* their act. Another person's seal of the same decision is never returned.
|
|
160
|
+
*/
|
|
161
|
+
latestOfDecisionBy(decisionRef: string, approverUserId: string): Promise<ApprovalSeal | null>;
|
|
116
162
|
/**
|
|
117
163
|
* Seals a relay should handle now, oldest due first: PENDING seals whose next attempt is due, and IN_FLIGHT seals whose
|
|
118
164
|
* lease expired. Two simple queries, each bounded and ordered by its due instant then `_id`. A due row this version
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-seal-relay-store.backend.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-relay-store.backend.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"approval-seal-relay-store.backend.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seal-relay-store.backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -93,6 +93,8 @@ export function signedFactsOfJws(jws) {
|
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
95
|
/** See the file's header. */
|
|
96
|
+
/** How many seals of one file the file's view reads to find the one its record stores (#2913). */
|
|
97
|
+
export const ATTESTING_SEAL_SCAN_LIMIT = 10;
|
|
96
98
|
export class ApprovalSealRelayStore {
|
|
97
99
|
repositoriesRegistry;
|
|
98
100
|
clock;
|
|
@@ -120,6 +122,72 @@ export class ApprovalSealRelayStore {
|
|
|
120
122
|
const [retained] = await this.repositoriesRegistry.findRetainedForCompletionForInfrastructure(SEALS, { _id: sealId });
|
|
121
123
|
return retained ? parseSeal(retained, true) : null;
|
|
122
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* The record a seal its approver's erasure RETAINED is about, and nothing else of it (#2913): what a record's view of
|
|
127
|
+
* its seal needs to say "approver erased" only to a reader of that very record. No field of the retained row but its
|
|
128
|
+
* subject leaves this method, which is what keeps the record's view outside the reasons the completion door is
|
|
129
|
+
* unaudited (`retained-completion-door-callers.guard.test.ts`).
|
|
130
|
+
*/
|
|
131
|
+
async retainedSubjectOf(sealId) {
|
|
132
|
+
const [retained] = await this.repositoriesRegistry.findRetainedForCompletionForInfrastructure(SEALS, { _id: sealId }, undefined, undefined, { sort: [{ field: '_id', order: SortOrder.ASC }], limit: 1 });
|
|
133
|
+
if (!retained || typeof retained.linkedResourceType !== 'string' || typeof retained.linkedResourceId !== 'string')
|
|
134
|
+
return null;
|
|
135
|
+
return { resourceType: retained.linkedResourceType, resourceId: retained.linkedResourceId };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The seal that superseded one (#2913): a later decision on the same record whose seal names it in `supersedesSealId`.
|
|
139
|
+
* The id of a live successor; `retained: true` without an id for one its own approver's erasure retained, so nothing of
|
|
140
|
+
* a retained row leaves but the fact that it exists. Null when nothing superseded it. Asked within the seal's own record
|
|
141
|
+
* (the indexed subject fields): what a record shows is whether IT was approved again, and a successor about another
|
|
142
|
+
* record (a reopened decision whose answer named another row) is not that record's supersession.
|
|
143
|
+
*/
|
|
144
|
+
async successorOf(sealId, subject) {
|
|
145
|
+
const filter = { linkedResourceType: subject.resourceType, linkedResourceId: subject.resourceId, supersedesSealId: sealId };
|
|
146
|
+
const [live] = await this.repositoriesRegistry.findManyForInfrastructure(SEALS, filter, undefined, undefined, { sort: [{ field: 'decidedAt', order: SortOrder.DESC }], limit: 1 });
|
|
147
|
+
if (live)
|
|
148
|
+
return { sealId: String(live._id) };
|
|
149
|
+
const [retained] = await this.repositoriesRegistry.findRetainedForCompletionForInfrastructure(SEALS, filter, undefined, undefined, { sort: [{ field: 'decidedAt', order: SortOrder.DESC }], limit: 1 });
|
|
150
|
+
return retained ? { retained: true } : null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The live seals that attest a file as their deliverable (#2913, `deliverableFileId`, indexed), latest decision first:
|
|
154
|
+
* what a file's view of its seal chooses from. Bounded: a file is attested by a handful of decisions at most.
|
|
155
|
+
*/
|
|
156
|
+
async attestingFile(fileId, limit = ATTESTING_SEAL_SCAN_LIMIT) {
|
|
157
|
+
const rows = await this.repositoriesRegistry.findManyForInfrastructure(SEALS, { deliverableFileId: fileId }, undefined, undefined, {
|
|
158
|
+
sort: [{ field: 'decidedAt', order: SortOrder.DESC }, { field: 'sequence', order: SortOrder.DESC }], limit,
|
|
159
|
+
});
|
|
160
|
+
return rows.flatMap((row) => {
|
|
161
|
+
const parsed = ApprovalSealSchema.safeParse(row);
|
|
162
|
+
return parsed.success ? [parsed.data] : [];
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The seals its approver's erasure RETAINED that attest a file, as their ids and subjects only (#2913): enough for a
|
|
167
|
+
* file's view to say "approver erased" to a reader of the file's record, after checking the seal's own record stores it.
|
|
168
|
+
* No other field of a retained row leaves this method.
|
|
169
|
+
*/
|
|
170
|
+
async retainedAttestingFile(fileId, limit = ATTESTING_SEAL_SCAN_LIMIT) {
|
|
171
|
+
const rows = await this.repositoriesRegistry.findRetainedForCompletionForInfrastructure(SEALS, { deliverableFileId: fileId }, undefined, undefined, {
|
|
172
|
+
sort: [{ field: 'decidedAt', order: SortOrder.DESC }], limit,
|
|
173
|
+
});
|
|
174
|
+
return rows.flatMap((row) => typeof row.linkedResourceType === 'string' && typeof row.linkedResourceId === 'string'
|
|
175
|
+
? [{ sealId: String(row._id), resourceType: row.linkedResourceType, resourceId: row.linkedResourceId }]
|
|
176
|
+
: []);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The latest live seal of one decision whose approver is `approverUserId` (#2913): the decider's own view of the seal of
|
|
180
|
+
* their act. Another person's seal of the same decision is never returned.
|
|
181
|
+
*/
|
|
182
|
+
async latestOfDecisionBy(decisionRef, approverUserId) {
|
|
183
|
+
const [row] = await this.repositoriesRegistry.findManyForInfrastructure(SEALS, { decisionRef, approverUserId }, undefined, undefined, {
|
|
184
|
+
sort: [{ field: 'sequence', order: SortOrder.DESC }, { field: 'decidedAt', order: SortOrder.DESC }], limit: 1,
|
|
185
|
+
});
|
|
186
|
+
if (!row)
|
|
187
|
+
return null;
|
|
188
|
+
const parsed = ApprovalSealSchema.safeParse(row);
|
|
189
|
+
return parsed.success ? parsed.data : null;
|
|
190
|
+
}
|
|
123
191
|
/**
|
|
124
192
|
* Seals a relay should handle now, oldest due first: PENDING seals whose next attempt is due, and IN_FLIGHT seals whose
|
|
125
193
|
* lease expired. Two simple queries, each bounded and ordered by its due instant then `_id`. A due row this version
|