@wildo-ai/saas-models 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/applications/applications-context.schemas.d.ts +14 -10
- package/dist/esm/applications/applications-context.schemas.d.ts.map +1 -1
- package/dist/esm/applications/applications.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/applications/applications.shared.resources-config.schemas.js +4 -3
- package/dist/esm/applications/applications.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/billing/billing-configuration.shared.schemas.d.ts +0 -4
- package/dist/esm/billing/billing-configuration.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/billing-configuration.shared.schemas.js +59 -17
- package/dist/esm/billing/billing-configuration.shared.schemas.js.map +1 -1
- package/dist/esm/billing/billing-enums.shared.schemas.d.ts +77 -21
- package/dist/esm/billing/billing-enums.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/billing-enums.shared.schemas.js +85 -23
- package/dist/esm/billing/billing-enums.shared.schemas.js.map +1 -1
- package/dist/esm/billing/billing-types.shared.schemas.d.ts +3 -3
- package/dist/esm/billing/price.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/price.shared.schemas.js +7 -1
- package/dist/esm/billing/price.shared.schemas.js.map +1 -1
- package/dist/esm/billing/product-behavior-policy.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/product-behavior-policy.shared.schemas.js +52 -11
- package/dist/esm/billing/product-behavior-policy.shared.schemas.js.map +1 -1
- package/dist/esm/billing/product-definition.shared.schemas.d.ts +2 -2
- package/dist/esm/billing/product.shared.resources-config.schemas.d.ts +2 -2
- package/dist/esm/billing/product.shared.schemas.d.ts +2 -2
- package/dist/esm/billing/product.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/product.shared.schemas.js +7 -3
- package/dist/esm/billing/product.shared.schemas.js.map +1 -1
- package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts +1 -1
- package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/billing/subscription.shared.resources-config.schemas.js +15 -1
- package/dist/esm/billing/subscription.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/billing/usage-record.shared.schemas.d.ts +7 -3
- package/dist/esm/billing/usage-record.shared.schemas.d.ts.map +1 -1
- package/dist/esm/billing/usage-record.shared.schemas.js +22 -3
- package/dist/esm/billing/usage-record.shared.schemas.js.map +1 -1
- package/dist/esm/companion/provider-discovery.shared.schemas.d.ts +69 -2
- package/dist/esm/companion/provider-discovery.shared.schemas.d.ts.map +1 -1
- package/dist/esm/companion/provider-discovery.shared.schemas.js +20 -1
- package/dist/esm/companion/provider-discovery.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/audit-trails/application-audit-logs.shared.resources-config.schemas.d.ts +85 -0
- package/dist/esm/compliance/audit-trails/application-audit-logs.shared.resources-config.schemas.d.ts.map +1 -0
- package/dist/esm/compliance/audit-trails/application-audit-logs.shared.resources-config.schemas.js +157 -0
- package/dist/esm/compliance/audit-trails/application-audit-logs.shared.resources-config.schemas.js.map +1 -0
- package/dist/esm/compliance/audit-trails/audit-trail-config.shared.schemas.d.ts +23 -0
- package/dist/esm/compliance/audit-trails/audit-trail-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/audit-trail-config.shared.schemas.js +56 -0
- package/dist/esm/compliance/audit-trails/audit-trail-config.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts +480 -20
- package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js +547 -23
- package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js.map +1 -1
- package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.d.ts.map +1 -1
- package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js +82 -3
- package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js.map +1 -1
- package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts +31 -30
- package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js +98 -3
- package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/privacy/privacy.shared.schemas.d.ts +17 -0
- package/dist/esm/compliance/privacy/privacy.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/privacy/privacy.shared.schemas.js +27 -4
- package/dist/esm/compliance/privacy/privacy.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts +82 -29
- package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/privacy/redaction.shared.schemas.js +95 -29
- package/dist/esm/compliance/privacy/redaction.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/privacy/subject-export.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/privacy/subject-export.shared.schemas.js +12 -5
- package/dist/esm/compliance/privacy/subject-export.shared.schemas.js.map +1 -1
- package/dist/esm/config/analytics-value-moments.shared.schemas.d.ts +21 -0
- package/dist/esm/config/analytics-value-moments.shared.schemas.d.ts.map +1 -1
- package/dist/esm/config/analytics-value-moments.shared.schemas.js +20 -0
- package/dist/esm/config/analytics-value-moments.shared.schemas.js.map +1 -1
- package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts +7 -5
- package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts.map +1 -1
- package/dist/esm/config/auth-config-resolution.shared.utils.d.ts +8 -1
- package/dist/esm/config/auth-config-resolution.shared.utils.d.ts.map +1 -1
- package/dist/esm/config/auth-config-resolution.shared.utils.js +29 -0
- package/dist/esm/config/auth-config-resolution.shared.utils.js.map +1 -1
- package/dist/esm/config/entity-identity-config.shared.schemas.d.ts +95 -17
- package/dist/esm/config/entity-identity-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/config/entity-identity-config.shared.schemas.js +86 -8
- package/dist/esm/config/entity-identity-config.shared.schemas.js.map +1 -1
- package/dist/esm/config/frontend/chart-definition.shared.schemas.d.ts +12 -12
- package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts +28 -0
- package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts.map +1 -1
- package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js +28 -0
- package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js.map +1 -1
- package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts +45 -12
- package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-data/external-data-pipeline.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts +103 -58
- package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/engine-capabilities.shared.schemas.js +176 -15
- package/dist/esm/external-providers/engine-capabilities.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/engine-capability-resolution-mode.shared.d.ts +15 -3
- package/dist/esm/external-providers/engine-capability-resolution-mode.shared.d.ts.map +1 -1
- package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js +32 -5
- package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js.map +1 -1
- package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts +83 -7
- package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/frontend-capability-public-config.shared.schemas.d.ts +59 -0
- package/dist/esm/external-providers/frontend-capability-public-config.shared.schemas.d.ts.map +1 -0
- package/dist/esm/external-providers/frontend-capability-public-config.shared.schemas.js +61 -0
- package/dist/esm/external-providers/frontend-capability-public-config.shared.schemas.js.map +1 -0
- package/dist/esm/external-providers/frontend-capability-runtime.shared.types.d.ts +97 -0
- package/dist/esm/external-providers/frontend-capability-runtime.shared.types.d.ts.map +1 -0
- package/dist/esm/external-providers/frontend-capability-runtime.shared.types.js +41 -0
- package/dist/esm/external-providers/frontend-capability-runtime.shared.types.js.map +1 -0
- package/dist/esm/external-providers/frontend-provider-entry.shared.schemas.d.ts +148 -2
- package/dist/esm/external-providers/frontend-provider-entry.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/frontend-provider-entry.shared.schemas.js +56 -1
- package/dist/esm/external-providers/frontend-provider-entry.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts +5 -28
- package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/provider-capability.shared.schemas.js +4 -2
- package/dist/esm/external-providers/provider-capability.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/provider-compliance-profile.shared.schemas.d.ts +18 -8
- package/dist/esm/external-providers/provider-compliance-profile.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/provider-compliance-profile.shared.schemas.js +9 -4
- package/dist/esm/external-providers/provider-compliance-profile.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/provider-frontend-surface.shared.schemas.d.ts +338 -0
- package/dist/esm/external-providers/provider-frontend-surface.shared.schemas.d.ts.map +1 -0
- package/dist/esm/external-providers/provider-frontend-surface.shared.schemas.js +332 -0
- package/dist/esm/external-providers/provider-frontend-surface.shared.schemas.js.map +1 -0
- package/dist/esm/external-providers/provider-metadata.shared.schemas.d.ts +178 -9
- package/dist/esm/external-providers/provider-metadata.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/provider-metadata.shared.schemas.js +181 -9
- package/dist/esm/external-providers/provider-metadata.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/provider-protocol.shared.schemas.d.ts +7 -5
- package/dist/esm/external-providers/provider-protocol.shared.schemas.d.ts.map +1 -1
- package/dist/esm/external-providers/provider-protocol.shared.schemas.js +7 -5
- package/dist/esm/external-providers/provider-protocol.shared.schemas.js.map +1 -1
- package/dist/esm/external-providers/provider-tier-rule.shared.utils.d.ts +40 -0
- package/dist/esm/external-providers/provider-tier-rule.shared.utils.d.ts.map +1 -0
- package/dist/esm/external-providers/provider-tier-rule.shared.utils.js +80 -0
- package/dist/esm/external-providers/provider-tier-rule.shared.utils.js.map +1 -0
- package/dist/esm/features/core-feature-definitions.shared.d.ts.map +1 -1
- package/dist/esm/features/core-feature-definitions.shared.js +7 -1
- package/dist/esm/features/core-feature-definitions.shared.js.map +1 -1
- package/dist/esm/features/feature-builders.shared.d.ts +32 -0
- package/dist/esm/features/feature-builders.shared.d.ts.map +1 -1
- package/dist/esm/features/feature-builders.shared.js +32 -0
- package/dist/esm/features/feature-builders.shared.js.map +1 -1
- package/dist/esm/files/file-upload-grant.shared.schemas.d.ts +14 -2
- package/dist/esm/files/file-upload-grant.shared.schemas.d.ts.map +1 -1
- package/dist/esm/files/file-upload-grant.shared.schemas.js +44 -5
- package/dist/esm/files/file-upload-grant.shared.schemas.js.map +1 -1
- package/dist/esm/files/files.shared.schemas.d.ts +15 -8
- package/dist/esm/files/files.shared.schemas.d.ts.map +1 -1
- package/dist/esm/files/files.shared.schemas.js +15 -8
- package/dist/esm/files/files.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/actors.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/actors.shared.schemas.js +3 -2
- package/dist/esm/flows-actors/actors.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/agents.shared.schemas.d.ts +54 -5
- package/dist/esm/flows-actors/agents.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/agents.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/apps-blueprint/apps-blueprint.shared.schemas.d.ts +6 -6
- package/dist/esm/flows-actors/deliberation.shared.schemas.d.ts +0 -3
- package/dist/esm/flows-actors/deliberation.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/deliberation.shared.schemas.js +0 -5
- package/dist/esm/flows-actors/deliberation.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.d.ts +14 -0
- package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js +40 -3
- package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/flows-actors.commons.shared.schemas.d.ts +19 -0
- package/dist/esm/flows-actors/flows-actors.commons.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/flows-actors.commons.shared.schemas.js +33 -0
- package/dist/esm/flows-actors/flows-actors.commons.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/flows-actors.io.shared.schemas.d.ts +14 -9
- package/dist/esm/flows-actors/flows-actors.io.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/flows-actors.io.shared.schemas.js +14 -9
- package/dist/esm/flows-actors/flows-actors.io.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/flows-actors.system.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/flows-actors.system.shared.schemas.js +3 -2
- package/dist/esm/flows-actors/flows-actors.system.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/flows.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/flows.shared.schemas.js +3 -2
- package/dist/esm/flows-actors/flows.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/mcp-servers.shared.schemas.d.ts +128 -0
- package/dist/esm/flows-actors/mcp-servers.shared.schemas.d.ts.map +1 -0
- package/dist/esm/flows-actors/mcp-servers.shared.schemas.js +142 -0
- package/dist/esm/flows-actors/mcp-servers.shared.schemas.js.map +1 -0
- package/dist/esm/flows-actors/meta-workflows/budget-authority.meta-workflows.shared.schemas.d.ts +0 -3
- package/dist/esm/flows-actors/meta-workflows/budget-authority.meta-workflows.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/budget-authority.meta-workflows.shared.schemas.js +1 -4
- package/dist/esm/flows-actors/meta-workflows/budget-authority.meta-workflows.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/context-selection.meta-workflows.shared.schemas.d.ts +0 -16
- package/dist/esm/flows-actors/meta-workflows/context-selection.meta-workflows.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/context-selection.meta-workflows.shared.schemas.js +1 -17
- package/dist/esm/flows-actors/meta-workflows/context-selection.meta-workflows.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/decisions.meta-workflows.shared.schemas.d.ts +3 -3
- package/dist/esm/flows-actors/meta-workflows/decisions.meta-workflows.shared.schemas.js +3 -3
- package/dist/esm/flows-actors/meta-workflows/decisions.meta-workflows.shared.schemas.js.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/execution-binding-common.meta-workflows.shared.schemas.d.ts +2 -8
- package/dist/esm/flows-actors/meta-workflows/execution-binding-common.meta-workflows.shared.schemas.d.ts.map +1 -1
- package/dist/esm/flows-actors/meta-workflows/execution-binding-common.meta-workflows.shared.schemas.js +4 -15
- package/dist/esm/flows-actors/meta-workflows/execution-binding-common.meta-workflows.shared.schemas.js.map +1 -1
- package/dist/esm/guidance/guidance-state.shared.utils.d.ts.map +1 -1
- package/dist/esm/guidance/guidance-state.shared.utils.js +1 -0
- package/dist/esm/guidance/guidance-state.shared.utils.js.map +1 -1
- package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts +8 -0
- package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/guidance/progression-state.shared.schemas.d.ts +46 -0
- package/dist/esm/guidance/progression-state.shared.schemas.d.ts.map +1 -1
- package/dist/esm/guidance/progression-state.shared.schemas.js +49 -0
- package/dist/esm/guidance/progression-state.shared.schemas.js.map +1 -1
- package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts +320 -50
- package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts.map +1 -1
- package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js +137 -36
- package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js.map +1 -1
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/notifications/email/email-template-registry.shared.schemas.d.ts +11 -2
- package/dist/esm/notifications/email/email-template-registry.shared.schemas.d.ts.map +1 -1
- package/dist/esm/notifications/email/email-template-registry.shared.schemas.js.map +1 -1
- package/dist/esm/notifications/email/email-template.shared.enums.d.ts +15 -0
- package/dist/esm/notifications/email/email-template.shared.enums.d.ts.map +1 -1
- package/dist/esm/notifications/email/email-template.shared.enums.js +15 -0
- package/dist/esm/notifications/email/email-template.shared.enums.js.map +1 -1
- package/dist/esm/notifications/m2m/webhook-config.shared.resources-config.schemas.d.ts +1 -0
- package/dist/esm/notifications/m2m/webhook-config.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/notifications/m2m/webhook-config.shared.resources-config.schemas.js +19 -0
- package/dist/esm/notifications/m2m/webhook-config.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/notifications/m2m/webhook-config.shared.schemas.d.ts +30 -0
- package/dist/esm/notifications/m2m/webhook-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/notifications/m2m/webhook-config.shared.schemas.js +65 -1
- package/dist/esm/notifications/m2m/webhook-config.shared.schemas.js.map +1 -1
- package/dist/esm/notifications/notification-badges.core.definitions.d.ts +6 -4
- package/dist/esm/notifications/notification-badges.core.definitions.d.ts.map +1 -1
- package/dist/esm/notifications/notification-badges.core.definitions.js +6 -4
- package/dist/esm/notifications/notification-badges.core.definitions.js.map +1 -1
- package/dist/esm/notifications/notification-badges.schemas.d.ts +167 -5
- package/dist/esm/notifications/notification-badges.schemas.d.ts.map +1 -1
- package/dist/esm/notifications/notification-badges.schemas.js +191 -6
- package/dist/esm/notifications/notification-badges.schemas.js.map +1 -1
- package/dist/esm/notifications/notifications.shared.schemas.d.ts +107 -0
- package/dist/esm/notifications/notifications.shared.schemas.d.ts.map +1 -1
- package/dist/esm/notifications/notifications.shared.schemas.js +149 -1
- package/dist/esm/notifications/notifications.shared.schemas.js.map +1 -1
- package/dist/esm/organizations/organization-notifications.shared.schemas.d.ts.map +1 -1
- package/dist/esm/organizations/organization-notifications.shared.schemas.js +22 -1
- package/dist/esm/organizations/organization-notifications.shared.schemas.js.map +1 -1
- package/dist/esm/organizations/organization-preferences.shared.schemas.d.ts +26 -0
- package/dist/esm/organizations/organization-preferences.shared.schemas.d.ts.map +1 -1
- package/dist/esm/organizations/organization-preferences.shared.schemas.js +25 -0
- package/dist/esm/organizations/organization-preferences.shared.schemas.js.map +1 -1
- package/dist/esm/organizations/organization-profile.shared.schemas.d.ts +25 -0
- package/dist/esm/organizations/organization-profile.shared.schemas.d.ts.map +1 -1
- package/dist/esm/organizations/organization-profile.shared.schemas.js +42 -17
- package/dist/esm/organizations/organization-profile.shared.schemas.js.map +1 -1
- package/dist/esm/organizations/organization-unit-narrowing.shared.d.ts +3 -2
- package/dist/esm/organizations/organization-unit-narrowing.shared.d.ts.map +1 -1
- package/dist/esm/organizations/organization-unit-narrowing.shared.js +9 -2
- package/dist/esm/organizations/organization-unit-narrowing.shared.js.map +1 -1
- package/dist/esm/organizations/organizations.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/organizations/organizations.shared.resources-config.schemas.js +18 -3
- package/dist/esm/organizations/organizations.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/public-runtime/index.d.ts +49 -2
- package/dist/esm/public-runtime/index.d.ts.map +1 -1
- package/dist/esm/public-runtime/index.js +48 -1
- package/dist/esm/public-runtime/index.js.map +1 -1
- package/dist/esm/queue/jobs.shared.resources-config.schemas.d.ts +6 -5
- package/dist/esm/queue/jobs.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/queue/jobs.shared.resources-config.schemas.js +6 -5
- package/dist/esm/queue/jobs.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/queue/jobs.shared.schemas.d.ts +16 -3
- package/dist/esm/queue/jobs.shared.schemas.d.ts.map +1 -1
- package/dist/esm/queue/jobs.shared.schemas.js +16 -3
- package/dist/esm/queue/jobs.shared.schemas.js.map +1 -1
- package/dist/esm/requests/manual-controllers-routes-documentation.d.ts +137 -0
- package/dist/esm/requests/manual-controllers-routes-documentation.d.ts.map +1 -0
- package/dist/esm/requests/manual-controllers-routes-documentation.js +1201 -0
- package/dist/esm/requests/manual-controllers-routes-documentation.js.map +1 -0
- package/dist/esm/requests/manual-controllers-routes-urls.d.ts +67 -0
- package/dist/esm/requests/manual-controllers-routes-urls.d.ts.map +1 -1
- package/dist/esm/requests/manual-controllers-routes-urls.js +78 -0
- package/dist/esm/requests/manual-controllers-routes-urls.js.map +1 -1
- package/dist/esm/requests/rate-limits.shared.schemas.d.ts +11 -1
- package/dist/esm/requests/rate-limits.shared.schemas.d.ts.map +1 -1
- package/dist/esm/requests/rate-limits.shared.schemas.js +11 -1
- package/dist/esm/requests/rate-limits.shared.schemas.js.map +1 -1
- package/dist/esm/requests/websocket.shared.schemas.d.ts +27 -4
- package/dist/esm/requests/websocket.shared.schemas.d.ts.map +1 -1
- package/dist/esm/requests/websocket.shared.schemas.js +35 -0
- package/dist/esm/requests/websocket.shared.schemas.js.map +1 -1
- package/dist/esm/resources/resources-config.shared.factory.d.ts +1 -0
- package/dist/esm/resources/resources-config.shared.factory.d.ts.map +1 -1
- package/dist/esm/resources/resources-config.shared.factory.js +105 -20
- package/dist/esm/resources/resources-config.shared.factory.js.map +1 -1
- package/dist/esm/resources/resources-config.shared.schemas.d.ts +254 -72
- package/dist/esm/resources/resources-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/resources/resources-config.shared.schemas.js.map +1 -1
- package/dist/esm/resources/resources-registry.shared.core.definitions.d.ts.map +1 -1
- package/dist/esm/resources/resources-registry.shared.core.definitions.js +97 -5
- package/dist/esm/resources/resources-registry.shared.core.definitions.js.map +1 -1
- package/dist/esm/resources/resources-relationships.shared.factory.d.ts.map +1 -1
- package/dist/esm/resources/resources-relationships.shared.factory.js +15 -0
- package/dist/esm/resources/resources-relationships.shared.factory.js.map +1 -1
- package/dist/esm/resources/resources.shared.core.definitions.d.ts +37 -0
- package/dist/esm/resources/resources.shared.core.definitions.d.ts.map +1 -1
- package/dist/esm/resources/resources.shared.core.definitions.js +51 -0
- package/dist/esm/resources/resources.shared.core.definitions.js.map +1 -1
- package/dist/esm/resources/resources.shared.schemas.d.ts +11 -2
- package/dist/esm/resources/resources.shared.schemas.d.ts.map +1 -1
- package/dist/esm/resources/resources.shared.schemas.js +34 -3
- package/dist/esm/resources/resources.shared.schemas.js.map +1 -1
- package/dist/esm/resources/utils/field-value-disclosure.utils.d.ts +51 -0
- package/dist/esm/resources/utils/field-value-disclosure.utils.d.ts.map +1 -0
- package/dist/esm/resources/utils/field-value-disclosure.utils.js +53 -0
- package/dist/esm/resources/utils/field-value-disclosure.utils.js.map +1 -0
- package/dist/esm/resources/utils/resources-summary.utils.d.ts +16 -2
- package/dist/esm/resources/utils/resources-summary.utils.d.ts.map +1 -1
- package/dist/esm/resources/utils/resources-summary.utils.js +19 -5
- package/dist/esm/resources/utils/resources-summary.utils.js.map +1 -1
- package/dist/esm/resources/utils/shared-persistence-collection.utils.d.ts.map +1 -1
- package/dist/esm/resources/utils/shared-persistence-collection.utils.js +11 -0
- package/dist/esm/resources/utils/shared-persistence-collection.utils.js.map +1 -1
- package/dist/esm/runtime/domain-module.shared.definitions.d.ts +25 -0
- package/dist/esm/runtime/domain-module.shared.definitions.d.ts.map +1 -1
- package/dist/esm/runtime/domain-module.shared.definitions.js.map +1 -1
- package/dist/esm/security/api-keys/api-keys-application.shared.resources-config.schemas.d.ts +1 -0
- package/dist/esm/security/api-keys/api-keys-application.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/api-keys/api-keys-application.shared.resources-config.schemas.js +38 -0
- package/dist/esm/security/api-keys/api-keys-application.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/api-keys/api-keys-organization.shared.resources-config.schemas.d.ts +1 -0
- package/dist/esm/security/api-keys/api-keys-organization.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/api-keys/api-keys-organization.shared.resources-config.schemas.js +38 -0
- package/dist/esm/security/api-keys/api-keys-organization.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/authentications/authentication.shared.dto.schemas.d.ts +4 -11
- package/dist/esm/security/authentications/authentication.shared.dto.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/authentication.shared.dto.schemas.js +0 -1
- package/dist/esm/security/authentications/authentication.shared.dto.schemas.js.map +1 -1
- package/dist/esm/security/authentications/authentication.shared.schemas.d.ts +84 -5
- package/dist/esm/security/authentications/authentication.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/authentication.shared.schemas.js +83 -3
- package/dist/esm/security/authentications/authentication.shared.schemas.js.map +1 -1
- package/dist/esm/security/authentications/registration.shared.dto.schemas.d.ts +1 -0
- package/dist/esm/security/authentications/registration.shared.dto.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/registration.shared.dto.schemas.js +11 -0
- package/dist/esm/security/authentications/registration.shared.dto.schemas.js.map +1 -1
- package/dist/esm/security/authentications/scim/scim-provisioning-config.shared.schemas.d.ts +15 -0
- package/dist/esm/security/authentications/scim/scim-provisioning-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/scim/scim-provisioning-config.shared.schemas.js +21 -4
- package/dist/esm/security/authentications/scim/scim-provisioning-config.shared.schemas.js.map +1 -1
- package/dist/esm/security/authentications/sso/sso-config.shared.resources-config.schemas.d.ts +61 -1
- package/dist/esm/security/authentications/sso/sso-config.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/sso/sso-config.shared.resources-config.schemas.js +208 -6
- package/dist/esm/security/authentications/sso/sso-config.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/authentications/sso/sso-config.shared.schemas.d.ts +37 -27
- package/dist/esm/security/authentications/sso/sso-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/sso/sso-config.shared.schemas.js +134 -12
- package/dist/esm/security/authentications/sso/sso-config.shared.schemas.js.map +1 -1
- package/dist/esm/security/authentications/sso/sso-connection.shared.schemas.d.ts +0 -6
- package/dist/esm/security/authentications/sso/sso-connection.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/authentications/sso/sso-connection.shared.schemas.js +17 -8
- package/dist/esm/security/authentications/sso/sso-connection.shared.schemas.js.map +1 -1
- package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js +27 -0
- package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts +52 -7
- package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js +45 -8
- package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js.map +1 -1
- package/dist/esm/security/connected-accounts/provider-credentials-organization.shared.resources-config.schemas.d.ts +31 -0
- package/dist/esm/security/connected-accounts/provider-credentials-organization.shared.resources-config.schemas.d.ts.map +1 -0
- package/dist/esm/security/connected-accounts/provider-credentials-organization.shared.resources-config.schemas.js +198 -0
- package/dist/esm/security/connected-accounts/provider-credentials-organization.shared.resources-config.schemas.js.map +1 -0
- package/dist/esm/security/connected-accounts/provider-credentials-user.shared.resources-config.schemas.d.ts +31 -0
- package/dist/esm/security/connected-accounts/provider-credentials-user.shared.resources-config.schemas.d.ts.map +1 -0
- package/dist/esm/security/connected-accounts/provider-credentials-user.shared.resources-config.schemas.js +191 -0
- package/dist/esm/security/connected-accounts/provider-credentials-user.shared.resources-config.schemas.js.map +1 -0
- package/dist/esm/security/connected-accounts/provider-credentials.shared.schemas.d.ts +250 -0
- package/dist/esm/security/connected-accounts/provider-credentials.shared.schemas.d.ts.map +1 -0
- package/dist/esm/security/connected-accounts/provider-credentials.shared.schemas.js +486 -0
- package/dist/esm/security/connected-accounts/provider-credentials.shared.schemas.js.map +1 -0
- package/dist/esm/security/oauth-clients/oauth-clients-application.shared.resources-config.schemas.d.ts +4 -1
- package/dist/esm/security/oauth-clients/oauth-clients-application.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients-application.shared.resources-config.schemas.js +29 -0
- package/dist/esm/security/oauth-clients/oauth-clients-application.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients-organization.shared.resources-config.schemas.d.ts +4 -1
- package/dist/esm/security/oauth-clients/oauth-clients-organization.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients-organization.shared.resources-config.schemas.js +29 -0
- package/dist/esm/security/oauth-clients/oauth-clients-organization.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts +6 -0
- package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js +31 -0
- package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.d.ts +13 -0
- package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.d.ts.map +1 -1
- package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js +24 -3
- package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js.map +1 -1
- package/dist/esm/users/user-preferences.shared.resources-config.schemas.d.ts +4 -4
- package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.d.ts +4 -4
- package/dist/esm/users/user-self.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/users/user-self.shared.resources-config.schemas.js +33 -3
- package/dist/esm/users/user-self.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/users/users.shared.resources-config.schemas.d.ts +17 -1
- package/dist/esm/users/users.shared.resources-config.schemas.d.ts.map +1 -1
- package/dist/esm/users/users.shared.resources-config.schemas.js +136 -5
- package/dist/esm/users/users.shared.resources-config.schemas.js.map +1 -1
- package/dist/esm/users/users.shared.schemas.d.ts +43 -12
- package/dist/esm/users/users.shared.schemas.d.ts.map +1 -1
- package/dist/esm/users/users.shared.schemas.js +38 -7
- package/dist/esm/users/users.shared.schemas.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -32,12 +32,107 @@ import { z } from 'zod';
|
|
|
32
32
|
* consumes it. An application may always declare where it operates; Wildo says plainly where it has
|
|
33
33
|
* nothing to offer.
|
|
34
34
|
*/
|
|
35
|
+
const JURISDICTION_SLUG_SHAPE = /^[a-z]{2}(-[a-z0-9]{1,8})?$/u;
|
|
36
|
+
const JURISDICTION_SLUG_SHAPE_MESSAGE = 'a jurisdiction slug is an ISO 3166-1 alpha-2 country code, a supranational code such as "eu", '
|
|
37
|
+
+ 'optionally with a regime or subdivision suffix such as "us-ca" — lowercase, because a slug that '
|
|
38
|
+
+ 'differs only by case is a second identifier for one place';
|
|
39
|
+
/**
|
|
40
|
+
* The CANONICAL form of a region subtag, or `null` when the subtag is not well-formed.
|
|
41
|
+
*
|
|
42
|
+
* Shape alone cannot separate `gb` from `uk`: both are two lowercase letters, both name the same
|
|
43
|
+
* place, and only one is the ISO 3166-1 alpha-2 code. So the shape check is answered by the regex
|
|
44
|
+
* and IDENTITY is answered here, by the same authority the publication validator already uses for
|
|
45
|
+
* the locale half of a variant — `Intl.getCanonicalLocales`, which resolves the CLDR alias table.
|
|
46
|
+
*
|
|
47
|
+
* A data file was rejected. An enumerated country list is a legal fact with a review trigger (the
|
|
48
|
+
* one table this file does keep, {@link JURISDICTION_CONTAINMENT}, says so and is 27 rows), and a
|
|
49
|
+
* second one covering every country on earth would rot with nobody watching. `Intl` ships the table
|
|
50
|
+
* with the runtime and updates with it.
|
|
51
|
+
*
|
|
52
|
+
* What this deliberately does NOT catch: a well-formed but unassigned code (`qq`, `aa`) canonicalises
|
|
53
|
+
* to itself and passes. Those are not authoring mistakes anybody makes; `uk` demonstrably is — it is
|
|
54
|
+
* in this repository's own tests and prose.
|
|
55
|
+
*/
|
|
56
|
+
function canonicalRegionSubtag(subtag) {
|
|
57
|
+
const requested = `und-${subtag.toUpperCase()}`;
|
|
58
|
+
try {
|
|
59
|
+
const [canonical] = Intl.getCanonicalLocales(requested);
|
|
60
|
+
return canonical === undefined ? null : canonical.slice('und-'.length);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* What is wrong with `slug` as a jurisdiction slug, or `null` when nothing is.
|
|
68
|
+
*
|
|
69
|
+
* Exported as a FUNCTION rather than only as a schema because the consumers that matter do not parse
|
|
70
|
+
* — the governed-document publication validator and the `_core` compliance-document candidate
|
|
71
|
+
* validator both emit their own typed issue records, and routing them through a Zod error to recover
|
|
72
|
+
* a message is how two surfaces come to disagree about what a jurisdiction is. One rule, three
|
|
73
|
+
* readers.
|
|
74
|
+
*/
|
|
75
|
+
export function jurisdictionSlugProblem(slug) {
|
|
76
|
+
if (!JURISDICTION_SLUG_SHAPE.test(slug)) {
|
|
77
|
+
return `${JURISDICTION_SLUG_SHAPE_MESSAGE} — received "${slug}"`;
|
|
78
|
+
}
|
|
79
|
+
const [root] = slug.split('-');
|
|
80
|
+
const canonical = canonicalRegionSubtag(root);
|
|
81
|
+
if (canonical === null) {
|
|
82
|
+
return `"${root}" is not a well-formed region code`;
|
|
83
|
+
}
|
|
84
|
+
if (canonical.toLowerCase() !== root) {
|
|
85
|
+
return `"${root}" is an alias for "${canonical.toLowerCase()}" — use the canonical code, or two slugs are one place`;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
35
89
|
export const JurisdictionSlugSchema = z
|
|
36
90
|
.string()
|
|
37
91
|
.trim()
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
92
|
+
.superRefine((slug, context) => {
|
|
93
|
+
const problem = jurisdictionSlugProblem(slug);
|
|
94
|
+
if (problem !== null) {
|
|
95
|
+
context.addIssue({ code: 'custom', message: problem });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* The namespace every jurisdiction REFERENCE carries.
|
|
100
|
+
*
|
|
101
|
+
* A ref is `jurisdiction:<slug>`, and both halves are checked, because checking only the namespace is
|
|
102
|
+
* what let `jurisdiction:united-kingdom` — a value {@link JurisdictionSlugSchema} would refuse
|
|
103
|
+
* outright — sit in a landed application specification beside that same application's configuration
|
|
104
|
+
* declaring `gb`.
|
|
105
|
+
*/
|
|
106
|
+
export const JURISDICTION_REF_NAMESPACE = 'jurisdiction:';
|
|
107
|
+
/** Which half of a `jurisdiction:<slug>` ref is wrong. The two are separate failures with separate remedies. */
|
|
108
|
+
export var JurisdictionRefProblemScope;
|
|
109
|
+
(function (JurisdictionRefProblemScope) {
|
|
110
|
+
/** The ref is not in the `jurisdiction:` namespace, so it resolves against a different vocabulary entirely. */
|
|
111
|
+
JurisdictionRefProblemScope["NAMESPACE"] = "NAMESPACE";
|
|
112
|
+
/** The namespace is right and the slug is not a jurisdiction. */
|
|
113
|
+
JurisdictionRefProblemScope["SLUG"] = "SLUG";
|
|
114
|
+
})(JurisdictionRefProblemScope || (JurisdictionRefProblemScope = {}));
|
|
115
|
+
/** What is wrong with `ref` as a `jurisdiction:<slug>` reference, or `null` when nothing is. */
|
|
116
|
+
export function jurisdictionRefProblem(ref) {
|
|
117
|
+
if (!ref.startsWith(JURISDICTION_REF_NAMESPACE)) {
|
|
118
|
+
return {
|
|
119
|
+
scope: JurisdictionRefProblemScope.NAMESPACE,
|
|
120
|
+
message: `jurisdiction ref ${ref} must use the ${JURISDICTION_REF_NAMESPACE} namespace`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const problem = jurisdictionSlugProblem(ref.slice(JURISDICTION_REF_NAMESPACE.length));
|
|
124
|
+
return problem === null ? null : { scope: JurisdictionRefProblemScope.SLUG, message: `jurisdiction ref ${ref}: ${problem}` };
|
|
125
|
+
}
|
|
126
|
+
/** `jurisdiction:<slug>`, with the slug held to the same rule as a declared operating jurisdiction. */
|
|
127
|
+
export const JurisdictionRefSchema = z
|
|
128
|
+
.string()
|
|
129
|
+
.trim()
|
|
130
|
+
.superRefine((ref, context) => {
|
|
131
|
+
const problem = jurisdictionRefProblem(ref);
|
|
132
|
+
if (problem !== null) {
|
|
133
|
+
context.addIssue({ code: 'custom', message: problem.message });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
41
136
|
/**
|
|
42
137
|
* One jurisdiction this application operates in, and why it reaches the product.
|
|
43
138
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operating-jurisdictions.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operating-jurisdictions.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CACJ,8BAA8B,EAC9B,gGAAgG;MAC5F,gGAAgG;MAChG,mDAAmD,CACxD,CAAC;AAGJ;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,YAAY,CAAC;IACxD,gBAAgB,EAAE,sBAAsB;IACxC,+GAA+G;IAC/G,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAChC,CAAC,CAAC;AAGH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC;KACrD,YAAY,CAAC;IACZ,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3D,CAAC;KACD,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC/E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IAChF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,QAAQ,CAAC;YACf,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,eAAe,CAAC;YACvB,OAAO,EACL,gBAAgB,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC;kBAC5G,+FAA+F;SACpG,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAgD,MAAM,CAAC,MAAM,CAAC;IACjG,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;QAClF,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;KAC7E,CAAC;CACH,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,QAAgB;IACrE,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACpC,mHAAmH;IACnH,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,CAAC,wBAAwB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACjD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CACrE,CAAC;AACJ,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * WHERE this application operates, as the one source every compliance surface reads (issue #229).\n *\n * ## The hole this closes\n *\n * Measured 2026-09-02: nothing in application configuration said which jurisdictions an application\n * reaches. `jurisdiction` appeared only as prose, as the Art. 27 origin of a representative, and as\n * `jurisdictionRefs` on a governed-document release. The assurance programme's `jurisdictionSlugs`\n * were free text supplied per run BY THE PRODUCING MODEL — which is the opposite of a source: it is\n * unverifiable, it can differ between two runs of the same application, and nothing downstream could\n * disagree with it.\n *\n * ## Why a sibling of `operatorComplianceIdentity` rather than a field inside it\n *\n * Both describe legal posture, so folding this in is tempting. It is the wrong shape for one reason\n * that outranks the tidiness: that schema is all-or-nothing inside, so a jurisdiction could not be\n * recorded until the lawful bases were settled with counsel. Where you sell is known long before why\n * you may process — and issue #228 exists precisely because filling the entity must never block.\n *\n * Keeping them siblings means each is answerable the day it is known.\n *\n * ## Why an open slug and not an enum\n *\n * A jurisdiction is a genuinely open identifier: an application may operate anywhere, and an enum\n * would have to be widened before an operator could state the truth about their own product. That is\n * the \"genuinely open app/provider/resource identifier\" exemption in the semantic-literal rule, not a\n * shortcut around it. The shape is constrained; the membership is not.\n *\n * The framework's ability to KNOW what a declared jurisdiction requires is a separate question, and\n * a deliberately visible one — see {@link JURISDICTION_CONTAINMENT} and the coverage report that\n * consumes it. An application may always declare where it operates; Wildo says plainly where it has\n * nothing to offer.\n */\nexport const JurisdictionSlugSchema = z\n .string()\n .trim()\n .regex(\n /^[a-z]{2}(-[a-z0-9]{1,8})?$/u,\n 'a jurisdiction slug is an ISO 3166-1 alpha-2 country code, a supranational code such as \"eu\", '\n + 'optionally with a subdivision suffix such as \"us-ca\" — lowercase, because a slug that differs '\n + 'only by case is a second identifier for one place',\n );\nexport type JurisdictionSlug = z.infer<typeof JurisdictionSlugSchema>;\n\n/**\n * One jurisdiction this application operates in, and why it reaches the product.\n *\n * `basis` is REQUIRED, and it is the half that makes the declaration mean anything — the same\n * argument the sibling schemas make for `assessmentNote` on a legitimate-interests basis and for\n * `gatedProcessing` on a consent gate. \"eu\" alone does not say whether the operator is established\n * there, sells there, or merely holds data about people there, and those imply different obligations.\n *\n * It is free prose deliberately. Nothing structured exists to key it to, and inventing a closed\n * vocabulary of reasons would be a legal taxonomy nobody reviewed.\n */\nexport const OperatingJurisdictionSchema = z.strictObject({\n jurisdictionSlug: JurisdictionSlugSchema,\n /** Why this jurisdiction reaches the product — \"customers established here\", \"data subjects resident here\". */\n basis: z.string().trim().min(1),\n});\nexport type OperatingJurisdiction = z.infer<typeof OperatingJurisdictionSchema>;\n\n/**\n * The jurisdictions this application operates in.\n *\n * ## Absent is UNDECLARED, and an empty list is not offered\n *\n * The sibling `clientStorage` inventory distinguishes absent (\"not inventoried\") from an empty array\n * (\"this surface sets nothing\"), because there both are meaningful. Here only one is: every product\n * operates somewhere, so an empty list would be a claim nobody could mean. `min(1)` therefore refuses\n * it, and the whole declaration stays optional at the configuration level — absence is the honest\n * \"nobody has answered\", and every consumer must render it as that rather than as \"nowhere\".\n */\nexport const ApplicationOperatingJurisdictionsSchema = z\n .strictObject({\n jurisdictions: z.array(OperatingJurisdictionSchema).min(1),\n })\n .superRefine((declaration, context) => {\n const slugs = declaration.jurisdictions.map((entry) => entry.jurisdictionSlug);\n const duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n if (duplicates.length > 0) {\n context.addIssue({\n code: 'custom',\n path: ['jurisdictions'],\n message:\n `jurisdiction ${[...new Set(duplicates)].map((slug) => `\"${slug}\"`).join(', ')} is declared more than once — `\n + 'one place has one basis, and two entries would let a document cite whichever suits the clause',\n });\n }\n });\nexport type ApplicationOperatingJurisdictions = z.infer<typeof ApplicationOperatingJurisdictionsSchema>;\n\n/**\n * Which jurisdictions a broader one contains, so a source addressing the broader one answers for them.\n *\n * Without this, an application declaring `fr` would be reported as uncovered while the GDPR source\n * plainly reaches it — over-warning, which is the failure mode that trains a reader to skim past the\n * warnings that matter. Subdivisions need no table: `us-ca` is contained by `us` by construction, and\n * that rule is applied in code rather than enumerated here.\n *\n * ⚠️ EU membership is a legal fact with a review trigger, not a convenience list: it changes when a\n * state accedes or withdraws, and it has done both. Verified against the 27 members as of 2026-09-02.\n * Nothing derives it, so a change is a deliberate edit here.\n */\nexport const JURISDICTION_CONTAINMENT: Readonly<Record<string, readonly string[]>> = Object.freeze({\n eu: Object.freeze([\n 'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gr', 'hr', 'hu',\n 'ie', 'it', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro', 'se', 'si', 'sk',\n ]),\n});\n\n/**\n * Does a source addressing `reach` answer for an application operating in `declared`?\n *\n * Three ways, and the order is deliberate — exact first, because it is the common case and the other\n * two are refinements of it.\n */\nexport function jurisdictionReachCovers(reach: string, declared: string): boolean {\n if (reach === declared) return true;\n // A source addressing the whole of `us` answers for `us-ca`; one addressing only `us-ca` does not answer for `us`.\n if (declared.startsWith(`${reach}-`)) return true;\n return (JURISDICTION_CONTAINMENT[reach] ?? []).some(\n (member) => member === declared || declared.startsWith(`${member}-`),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"operating-jurisdictions.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operating-jurisdictions.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAE/D,MAAM,+BAA+B,GACnC,gGAAgG;MAC9F,kGAAkG;MAClG,2DAA2D,CAAC;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,qBAAqB,CAAC,MAAc;IAC3C,MAAM,SAAS,GAAG,OAAO,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC;QACxD,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,OAAO,GAAG,+BAA+B,gBAAgB,IAAI,GAAG,CAAC;IACnE,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAK,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,IAAI,IAAI,oCAAoC,CAAC;IACtD,CAAC;IACD,IAAI,SAAS,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,IAAI,sBAAsB,SAAS,CAAC,WAAW,EAAE,wDAAwD,CAAC;IACvH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,EAAE;KACR,IAAI,EAAE;KACN,WAAW,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;IAC7B,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D,gHAAgH;AAChH,MAAM,CAAN,IAAY,2BAKX;AALD,WAAY,2BAA2B;IACrC,+GAA+G;IAC/G,sDAAuB,CAAA;IACvB,iEAAiE;IACjE,4CAAa,CAAA;AACf,CAAC,EALW,2BAA2B,KAA3B,2BAA2B,QAKtC;AAOD,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,KAAK,EAAE,2BAA2B,CAAC,SAAS;YAC5C,OAAO,EAAE,oBAAoB,GAAG,iBAAiB,0BAA0B,YAAY;SACxF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC,CAAC;IACtF,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,2BAA2B,CAAC,IAAI,EAAE,OAAO,EAAE,oBAAoB,GAAG,KAAK,OAAO,EAAE,EAAE,CAAC;AAC/H,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,MAAM,EAAE;KACR,IAAI,EAAE;KACN,WAAW,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;IAC5B,MAAM,OAAO,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEL;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,YAAY,CAAC;IACxD,gBAAgB,EAAE,sBAAsB;IACxC,+GAA+G;IAC/G,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAChC,CAAC,CAAC;AAGH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC;KACrD,YAAY,CAAC;IACZ,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3D,CAAC;KACD,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC/E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IAChF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,QAAQ,CAAC;YACf,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,eAAe,CAAC;YACvB,OAAO,EACL,gBAAgB,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC;kBAC5G,+FAA+F;SACpG,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAgD,MAAM,CAAC,MAAM,CAAC;IACjG,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;QAClF,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;KAC7E,CAAC;CACH,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,QAAgB;IACrE,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACpC,mHAAmH;IACnH,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,CAAC,wBAAwB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACjD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CACrE,CAAC;AACJ,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * WHERE this application operates, as the one source every compliance surface reads (issue #229).\n *\n * ## The hole this closes\n *\n * Measured 2026-09-02: nothing in application configuration said which jurisdictions an application\n * reaches. `jurisdiction` appeared only as prose, as the Art. 27 origin of a representative, and as\n * `jurisdictionRefs` on a governed-document release. The assurance programme's `jurisdictionSlugs`\n * were free text supplied per run BY THE PRODUCING MODEL — which is the opposite of a source: it is\n * unverifiable, it can differ between two runs of the same application, and nothing downstream could\n * disagree with it.\n *\n * ## Why a sibling of `operatorComplianceIdentity` rather than a field inside it\n *\n * Both describe legal posture, so folding this in is tempting. It is the wrong shape for one reason\n * that outranks the tidiness: that schema is all-or-nothing inside, so a jurisdiction could not be\n * recorded until the lawful bases were settled with counsel. Where you sell is known long before why\n * you may process — and issue #228 exists precisely because filling the entity must never block.\n *\n * Keeping them siblings means each is answerable the day it is known.\n *\n * ## Why an open slug and not an enum\n *\n * A jurisdiction is a genuinely open identifier: an application may operate anywhere, and an enum\n * would have to be widened before an operator could state the truth about their own product. That is\n * the \"genuinely open app/provider/resource identifier\" exemption in the semantic-literal rule, not a\n * shortcut around it. The shape is constrained; the membership is not.\n *\n * The framework's ability to KNOW what a declared jurisdiction requires is a separate question, and\n * a deliberately visible one — see {@link JURISDICTION_CONTAINMENT} and the coverage report that\n * consumes it. An application may always declare where it operates; Wildo says plainly where it has\n * nothing to offer.\n */\nconst JURISDICTION_SLUG_SHAPE = /^[a-z]{2}(-[a-z0-9]{1,8})?$/u;\n\nconst JURISDICTION_SLUG_SHAPE_MESSAGE =\n 'a jurisdiction slug is an ISO 3166-1 alpha-2 country code, a supranational code such as \"eu\", '\n + 'optionally with a regime or subdivision suffix such as \"us-ca\" — lowercase, because a slug that '\n + 'differs only by case is a second identifier for one place';\n\n/**\n * The CANONICAL form of a region subtag, or `null` when the subtag is not well-formed.\n *\n * Shape alone cannot separate `gb` from `uk`: both are two lowercase letters, both name the same\n * place, and only one is the ISO 3166-1 alpha-2 code. So the shape check is answered by the regex\n * and IDENTITY is answered here, by the same authority the publication validator already uses for\n * the locale half of a variant — `Intl.getCanonicalLocales`, which resolves the CLDR alias table.\n *\n * A data file was rejected. An enumerated country list is a legal fact with a review trigger (the\n * one table this file does keep, {@link JURISDICTION_CONTAINMENT}, says so and is 27 rows), and a\n * second one covering every country on earth would rot with nobody watching. `Intl` ships the table\n * with the runtime and updates with it.\n *\n * What this deliberately does NOT catch: a well-formed but unassigned code (`qq`, `aa`) canonicalises\n * to itself and passes. Those are not authoring mistakes anybody makes; `uk` demonstrably is — it is\n * in this repository's own tests and prose.\n */\nfunction canonicalRegionSubtag(subtag: string): string | null {\n const requested = `und-${subtag.toUpperCase()}`;\n try {\n const [canonical] = Intl.getCanonicalLocales(requested);\n return canonical === undefined ? null : canonical.slice('und-'.length);\n } catch {\n return null;\n }\n}\n\n/**\n * What is wrong with `slug` as a jurisdiction slug, or `null` when nothing is.\n *\n * Exported as a FUNCTION rather than only as a schema because the consumers that matter do not parse\n * — the governed-document publication validator and the `_core` compliance-document candidate\n * validator both emit their own typed issue records, and routing them through a Zod error to recover\n * a message is how two surfaces come to disagree about what a jurisdiction is. One rule, three\n * readers.\n */\nexport function jurisdictionSlugProblem(slug: string): string | null {\n if (!JURISDICTION_SLUG_SHAPE.test(slug)) {\n return `${JURISDICTION_SLUG_SHAPE_MESSAGE} — received \"${slug}\"`;\n }\n const [root] = slug.split('-');\n const canonical = canonicalRegionSubtag(root!);\n if (canonical === null) {\n return `\"${root}\" is not a well-formed region code`;\n }\n if (canonical.toLowerCase() !== root) {\n return `\"${root}\" is an alias for \"${canonical.toLowerCase()}\" — use the canonical code, or two slugs are one place`;\n }\n return null;\n}\n\nexport const JurisdictionSlugSchema = z\n .string()\n .trim()\n .superRefine((slug, context) => {\n const problem = jurisdictionSlugProblem(slug);\n if (problem !== null) {\n context.addIssue({ code: 'custom', message: problem });\n }\n });\nexport type JurisdictionSlug = z.infer<typeof JurisdictionSlugSchema>;\n\n/**\n * The namespace every jurisdiction REFERENCE carries.\n *\n * A ref is `jurisdiction:<slug>`, and both halves are checked, because checking only the namespace is\n * what let `jurisdiction:united-kingdom` — a value {@link JurisdictionSlugSchema} would refuse\n * outright — sit in a landed application specification beside that same application's configuration\n * declaring `gb`.\n */\nexport const JURISDICTION_REF_NAMESPACE = 'jurisdiction:';\n\n/** Which half of a `jurisdiction:<slug>` ref is wrong. The two are separate failures with separate remedies. */\nexport enum JurisdictionRefProblemScope {\n /** The ref is not in the `jurisdiction:` namespace, so it resolves against a different vocabulary entirely. */\n NAMESPACE = 'NAMESPACE',\n /** The namespace is right and the slug is not a jurisdiction. */\n SLUG = 'SLUG',\n}\n\nexport interface JurisdictionRefProblem {\n readonly scope: JurisdictionRefProblemScope;\n readonly message: string;\n}\n\n/** What is wrong with `ref` as a `jurisdiction:<slug>` reference, or `null` when nothing is. */\nexport function jurisdictionRefProblem(ref: string): JurisdictionRefProblem | null {\n if (!ref.startsWith(JURISDICTION_REF_NAMESPACE)) {\n return {\n scope: JurisdictionRefProblemScope.NAMESPACE,\n message: `jurisdiction ref ${ref} must use the ${JURISDICTION_REF_NAMESPACE} namespace`,\n };\n }\n const problem = jurisdictionSlugProblem(ref.slice(JURISDICTION_REF_NAMESPACE.length));\n return problem === null ? null : { scope: JurisdictionRefProblemScope.SLUG, message: `jurisdiction ref ${ref}: ${problem}` };\n}\n\n/** `jurisdiction:<slug>`, with the slug held to the same rule as a declared operating jurisdiction. */\nexport const JurisdictionRefSchema = z\n .string()\n .trim()\n .superRefine((ref, context) => {\n const problem = jurisdictionRefProblem(ref);\n if (problem !== null) {\n context.addIssue({ code: 'custom', message: problem.message });\n }\n });\n\n/**\n * One jurisdiction this application operates in, and why it reaches the product.\n *\n * `basis` is REQUIRED, and it is the half that makes the declaration mean anything — the same\n * argument the sibling schemas make for `assessmentNote` on a legitimate-interests basis and for\n * `gatedProcessing` on a consent gate. \"eu\" alone does not say whether the operator is established\n * there, sells there, or merely holds data about people there, and those imply different obligations.\n *\n * It is free prose deliberately. Nothing structured exists to key it to, and inventing a closed\n * vocabulary of reasons would be a legal taxonomy nobody reviewed.\n */\nexport const OperatingJurisdictionSchema = z.strictObject({\n jurisdictionSlug: JurisdictionSlugSchema,\n /** Why this jurisdiction reaches the product — \"customers established here\", \"data subjects resident here\". */\n basis: z.string().trim().min(1),\n});\nexport type OperatingJurisdiction = z.infer<typeof OperatingJurisdictionSchema>;\n\n/**\n * The jurisdictions this application operates in.\n *\n * ## Absent is UNDECLARED, and an empty list is not offered\n *\n * The sibling `clientStorage` inventory distinguishes absent (\"not inventoried\") from an empty array\n * (\"this surface sets nothing\"), because there both are meaningful. Here only one is: every product\n * operates somewhere, so an empty list would be a claim nobody could mean. `min(1)` therefore refuses\n * it, and the whole declaration stays optional at the configuration level — absence is the honest\n * \"nobody has answered\", and every consumer must render it as that rather than as \"nowhere\".\n */\nexport const ApplicationOperatingJurisdictionsSchema = z\n .strictObject({\n jurisdictions: z.array(OperatingJurisdictionSchema).min(1),\n })\n .superRefine((declaration, context) => {\n const slugs = declaration.jurisdictions.map((entry) => entry.jurisdictionSlug);\n const duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n if (duplicates.length > 0) {\n context.addIssue({\n code: 'custom',\n path: ['jurisdictions'],\n message:\n `jurisdiction ${[...new Set(duplicates)].map((slug) => `\"${slug}\"`).join(', ')} is declared more than once — `\n + 'one place has one basis, and two entries would let a document cite whichever suits the clause',\n });\n }\n });\nexport type ApplicationOperatingJurisdictions = z.infer<typeof ApplicationOperatingJurisdictionsSchema>;\n\n/**\n * Which jurisdictions a broader one contains, so a source addressing the broader one answers for them.\n *\n * Without this, an application declaring `fr` would be reported as uncovered while the GDPR source\n * plainly reaches it — over-warning, which is the failure mode that trains a reader to skim past the\n * warnings that matter. Subdivisions need no table: `us-ca` is contained by `us` by construction, and\n * that rule is applied in code rather than enumerated here.\n *\n * ⚠️ EU membership is a legal fact with a review trigger, not a convenience list: it changes when a\n * state accedes or withdraws, and it has done both. Verified against the 27 members as of 2026-09-02.\n * Nothing derives it, so a change is a deliberate edit here.\n */\nexport const JURISDICTION_CONTAINMENT: Readonly<Record<string, readonly string[]>> = Object.freeze({\n eu: Object.freeze([\n 'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gr', 'hr', 'hu',\n 'ie', 'it', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro', 'se', 'si', 'sk',\n ]),\n});\n\n/**\n * Does a source addressing `reach` answer for an application operating in `declared`?\n *\n * Three ways, and the order is deliberate — exact first, because it is the common case and the other\n * two are refinements of it.\n */\nexport function jurisdictionReachCovers(reach: string, declared: string): boolean {\n if (reach === declared) return true;\n // A source addressing the whole of `us` answers for `us-ca`; one addressing only `us-ca` does not answer for `us`.\n if (declared.startsWith(`${reach}-`)) return true;\n return (JURISDICTION_CONTAINMENT[reach] ?? []).some(\n (member) => member === declared || declared.startsWith(`${member}-`),\n );\n}\n"]}
|
|
@@ -59,6 +59,22 @@ export declare enum PrivacyLevel {
|
|
|
59
59
|
* Not legal advice. See `.claude/plans/traceability-correlation-id.md` (SD9) for the full analysis
|
|
60
60
|
* and the compliance obligations that attach to each non-`DISABLED` member.
|
|
61
61
|
*/
|
|
62
|
+
/**
|
|
63
|
+
* @wildo_source:part:start saas.models.privacy.client-instance-id-scope facet:layer:shared facet:audience:app-developer
|
|
64
|
+
*
|
|
65
|
+
* Whether the application identifies a browser INSTALL across visits, declared as
|
|
66
|
+
* `compliance.clientInstanceIdScope`.
|
|
67
|
+
*
|
|
68
|
+
* It lives on `compliance` rather than beside the observability settings because it is a PRIVACY
|
|
69
|
+
* decision, and on the SHARED config because both halves must obey one authored value: the frontend
|
|
70
|
+
* decides whether to mint, store and send the id, and the backend independently decides whether to
|
|
71
|
+
* ingest and record it. Two gates, one source — so an application that never opts in is protected
|
|
72
|
+
* even if some client sends the header anyway.
|
|
73
|
+
*
|
|
74
|
+
* **`DISABLED` is the default**, so the privacy-preserving posture is what you get by doing nothing.
|
|
75
|
+
* Choosing another member is choosing an obligation, not just a signal: start-up prints the
|
|
76
|
+
* obligations each opt-in creates.
|
|
77
|
+
*/
|
|
62
78
|
export declare enum ClientInstanceIdScope {
|
|
63
79
|
/**
|
|
64
80
|
* DEFAULT. No client-install identifier is minted, sent, stored, or recorded — and the backend
|
|
@@ -82,6 +98,7 @@ export declare enum ClientInstanceIdScope {
|
|
|
82
98
|
*/
|
|
83
99
|
PERSISTENT = "persistent"
|
|
84
100
|
}
|
|
101
|
+
/** @wildo_source:part:end saas.models.privacy.client-instance-id-scope */
|
|
85
102
|
export declare const AnonymousUserPrivacySchema: z.ZodObject<{
|
|
86
103
|
dataProcessingConsent: z.ZodDefault<z.ZodEnum<typeof DataProcessingConsent>>;
|
|
87
104
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/privacy.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,oBAAY,qBAAqB;IAC/B,MAAM,WAAW,CAAE,sBAAsB;IACzC,oBAAoB,yBAAyB,CAAE,uDAAuD;IACtG,YAAY,iBAAiB,CAAE,mCAAmC;IAClE,OAAO,YAAY;CACpB;AAED,oBAAY,6BAA6B;IACvC,MAAM,WAAW,CAAE,sBAAsB;IACzC,oBAAoB,yBAAyB,CAAE,uDAAuD;IACtG,OAAO,YAAY;CACpB;AAED,oBAAY,qBAAqB;IAC/B,IAAI,SAAS;IACb,KAAK,UAAU;IACf,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,IAAI,SAAS;CACd;AAED,oBAAY,YAAY;IACtB,OAAO,YAAY,CAAE,0BAA0B;IAC/C,QAAQ,aAAa,CAAE,2BAA2B;IAClD,QAAQ,aAAa;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,oBAAY,qBAAqB;IAC/B;;;;;OAKG;IACH,QAAQ,aAAa;IACrB;;;;OAIG;IACH,OAAO,YAAY;IACnB;;;;;;OAMG;IACH,UAAU,eAAe;CAC1B;
|
|
1
|
+
{"version":3,"file":"privacy.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/privacy.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,oBAAY,qBAAqB;IAC/B,MAAM,WAAW,CAAE,sBAAsB;IACzC,oBAAoB,yBAAyB,CAAE,uDAAuD;IACtG,YAAY,iBAAiB,CAAE,mCAAmC;IAClE,OAAO,YAAY;CACpB;AAED,oBAAY,6BAA6B;IACvC,MAAM,WAAW,CAAE,sBAAsB;IACzC,oBAAoB,yBAAyB,CAAE,uDAAuD;IACtG,OAAO,YAAY;CACpB;AAED,oBAAY,qBAAqB;IAC/B,IAAI,SAAS;IACb,KAAK,UAAU;IACf,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,IAAI,SAAS;CACd;AAED,oBAAY,YAAY;IACtB,OAAO,YAAY,CAAE,0BAA0B;IAC/C,QAAQ,aAAa,CAAE,2BAA2B;IAClD,QAAQ,aAAa;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;;GAeG;AACH,oBAAY,qBAAqB;IAC/B;;;;;OAKG;IACH,QAAQ,aAAa;IACrB;;;;OAIG;IACH,OAAO,YAAY;IACnB;;;;;;OAMG;IACH,UAAU,eAAe;CAC1B;AACD,0EAA0E;AAE1E,eAAO,MAAM,0BAA0B;;iBAKrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,2BAA2B;;iBAKtC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,eAAO,MAAM,iBAAiB;;;iBAuB5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
|
@@ -63,6 +63,22 @@ export var PrivacyLevel;
|
|
|
63
63
|
* Not legal advice. See `.claude/plans/traceability-correlation-id.md` (SD9) for the full analysis
|
|
64
64
|
* and the compliance obligations that attach to each non-`DISABLED` member.
|
|
65
65
|
*/
|
|
66
|
+
/**
|
|
67
|
+
* @wildo_source:part:start saas.models.privacy.client-instance-id-scope facet:layer:shared facet:audience:app-developer
|
|
68
|
+
*
|
|
69
|
+
* Whether the application identifies a browser INSTALL across visits, declared as
|
|
70
|
+
* `compliance.clientInstanceIdScope`.
|
|
71
|
+
*
|
|
72
|
+
* It lives on `compliance` rather than beside the observability settings because it is a PRIVACY
|
|
73
|
+
* decision, and on the SHARED config because both halves must obey one authored value: the frontend
|
|
74
|
+
* decides whether to mint, store and send the id, and the backend independently decides whether to
|
|
75
|
+
* ingest and record it. Two gates, one source — so an application that never opts in is protected
|
|
76
|
+
* even if some client sends the header anyway.
|
|
77
|
+
*
|
|
78
|
+
* **`DISABLED` is the default**, so the privacy-preserving posture is what you get by doing nothing.
|
|
79
|
+
* Choosing another member is choosing an obligation, not just a signal: start-up prints the
|
|
80
|
+
* obligations each opt-in creates.
|
|
81
|
+
*/
|
|
66
82
|
export var ClientInstanceIdScope;
|
|
67
83
|
(function (ClientInstanceIdScope) {
|
|
68
84
|
/**
|
|
@@ -87,11 +103,14 @@ export var ClientInstanceIdScope;
|
|
|
87
103
|
*/
|
|
88
104
|
ClientInstanceIdScope["PERSISTENT"] = "persistent";
|
|
89
105
|
})(ClientInstanceIdScope || (ClientInstanceIdScope = {}));
|
|
106
|
+
/** @wildo_source:part:end saas.models.privacy.client-instance-id-scope */
|
|
90
107
|
export const AnonymousUserPrivacySchema = z.object({
|
|
91
|
-
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
108
|
+
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
109
|
+
.describe('The processing this anonymous visitor has agreed to. The application decides what each level permits — the framework stores the decision, it does not attach a fixed meaning to the levels.'),
|
|
92
110
|
});
|
|
93
111
|
export const InboundContactPrivacySchema = z.object({
|
|
94
|
-
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
112
|
+
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
113
|
+
.describe('The processing this lead has agreed to. The application decides what each level permits — the framework stores the decision, it does not attach a fixed meaning to the levels.'),
|
|
95
114
|
});
|
|
96
115
|
export const UserPrivacySchema = z.object({
|
|
97
116
|
// `.isAuditEvidence()` on these two, and deliberately NOT on the block that contains them.
|
|
@@ -108,7 +127,11 @@ export const UserPrivacySchema = z.object({
|
|
|
108
127
|
// decision and must leave a trail naming the previous and resulting value. Reached today by the
|
|
109
128
|
// preferences RESET, the generic UPDATE, and `update_privacy`, all of which route through the
|
|
110
129
|
// same emission point, so no verb can change consent without a record.
|
|
111
|
-
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
112
|
-
|
|
130
|
+
dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)
|
|
131
|
+
.describe('The processing this person has agreed to. It is the subject\'s own decision and only they (or an administrator acting for them) change it. The application decides what each level permits — the framework stores the decision and records every change, it does not attach a fixed meaning to the levels.')
|
|
132
|
+
.isAuditEvidence(),
|
|
133
|
+
privacyLevel: z.enum(PrivacyLevel).default(PrivacyLevel.STANDARD)
|
|
134
|
+
.describe('How much this person has agreed the application may collect about them, alongside the processing consent. The application decides what each level permits.')
|
|
135
|
+
.isAuditEvidence(),
|
|
113
136
|
});
|
|
114
137
|
//# sourceMappingURL=privacy.shared.schemas.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/privacy.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAN,IAAY,qBAKX;AALD,WAAY,qBAAqB;IAC/B,0CAAiB,CAAA;IACjB,sEAA6C,CAAA;IAC7C,sDAA6B,CAAA;IAC7B,4CAAmB,CAAA;AACrB,CAAC,EALW,qBAAqB,KAArB,qBAAqB,QAKhC;AAED,MAAM,CAAN,IAAY,6BAIX;AAJD,WAAY,6BAA6B;IACvC,kDAAiB,CAAA;IACjB,8EAA6C,CAAA;IAC7C,oDAAmB,CAAA;AACrB,CAAC,EAJW,6BAA6B,KAA7B,6BAA6B,QAIxC;AAED,MAAM,CAAN,IAAY,qBAMX;AAND,WAAY,qBAAqB;IAC/B,sCAAa,CAAA;IACb,wCAAe,CAAA;IACf,gDAAuB,CAAA;IACvB,gDAAuB,CAAA;IACvB,sCAAa,CAAA;AACf,CAAC,EANW,qBAAqB,KAArB,qBAAqB,QAMhC;AAED,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,qCAAqB,CAAA;IACrB,qCAAqB,CAAA;AACvB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAN,IAAY,qBAsBX;AAtBD,WAAY,qBAAqB;IAC/B;;;;;OAKG;IACH,8CAAqB,CAAA;IACrB;;;;OAIG;IACH,4CAAmB,CAAA;IACnB;;;;;;OAMG;IACH,kDAAyB,CAAA;AAC3B,CAAC,EAtBW,qBAAqB,KAArB,qBAAqB,QAsBhC;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC;CAC1F,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC;CAC1F,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,2FAA2F;IAC3F,EAAE;IACF,yFAAyF;IACzF,4FAA4F;IAC5F,yFAAyF;IACzF,6EAA6E;IAC7E,EAAE;IACF,8FAA8F;IAC9F,mGAAmG;IACnG,EAAE;IACF,4FAA4F;IAC5F,gGAAgG;IAChG,8FAA8F;IAC9F,uEAAuE;IACvE,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE;IAC3G,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,eAAe,EAAE;CACpF,CAAC,CAAC","sourcesContent":["/**\n * Privacy Shared Schemas\n *\n * Privacy-related schemas and enums for user data management\n */\n\nimport { z } from \"zod\";\n\n/**\n * Profile Visibility Enum\n *\n * Controls who can see a user's profile information\n */\nexport enum UserProfileVisibility {\n PUBLIC = \"public\", // Visible to everyone\n APPLICATION_INTERNAL = \"application_internal\", // Visible within application, for connected users only\n ORGANIZATION = \"organization\", // Visible within organization only\n PRIVATE = \"private\", // Visible to user only\n}\n\nexport enum OrganizationProfileVisibility {\n PUBLIC = \"public\", // Visible to everyone\n APPLICATION_INTERNAL = \"application_internal\", // Visible within application, for connected users only\n PRIVATE = \"private\", // Visible to user only\n}\n\nexport enum DataProcessingConsent {\n NONE = \"none\",\n BASIC = \"basic\",\n ANALYTICS = \"analytics\",\n MARKETING = \"marketing\",\n FULL = \"full\",\n}\n\nexport enum PrivacyLevel {\n MINIMAL = \"minimal\", // Minimum data collection\n STANDARD = \"standard\", // Standard data collection\n ENHANCED = \"enhanced\", // Enhanced features with more data\n}\n\n/**\n * Whether — and with what lifetime — this application identifies the CLIENT INSTALL\n * (`clientInstanceId`, sent as `x-client-instance-id` and recorded on the audit row).\n *\n * A `clientInstanceId` answers \"which browser/device install issued this request?\", which the\n * per-request `correlationId` (server-minted, never stored on the device) and `frontendServiceName`\n * (a static per-app build constant) deliberately cannot. It is what lets an investigator group a\n * hostile actor's requests across sessions and logins — genuine security value, and exactly why it\n * needs a policy rather than a default.\n *\n * **This is a closed vocabulary with the OFF state inside it, on purpose.** Modelling it as\n * `{ enabled: boolean; scope: … }` would admit the meaningless `enabled: false, scope: PERSISTENT`\n * state; one enum makes \"off\" and \"how long it lives\" a single, always-coherent decision.\n *\n * **Why the framework refuses to choose for you (Fork D / SD9).** Storing an identifier on the\n * user's terminal equipment engages ePrivacy Directive Art 5(3), whose \"strictly necessary\"\n * exemption the EDPB reads narrowly (Guidelines 2/2023) — and *duration* is itself a\n * misclassification signal, so a long-lived id is the hardest case. GDPR Recital 49 (network and\n * information security as a legitimate interest) is a lawful basis for the PROCESSING but does not\n * remove the ePrivacy STORAGE consent question. Meanwhile NIST SP 800-53 AU-3 / AU-6(3)\n * affirmatively want this correlation capability for audit review. Those pull in opposite\n * directions, and the resolution is jurisdiction- and DPA-specific — so Wildo ships the mechanism\n * OFF and each application decides, with counsel, which member it may lawfully select.\n *\n * Not legal advice. See `.claude/plans/traceability-correlation-id.md` (SD9) for the full analysis\n * and the compliance obligations that attach to each non-`DISABLED` member.\n */\nexport enum ClientInstanceIdScope {\n /**\n * DEFAULT. No client-install identifier is minted, sent, stored, or recorded — and the backend\n * additionally REFUSES an inbound `x-client-instance-id` (fail-closed, so a rogue or misconfigured\n * client cannot start populating a pseudonymous identifier in an application's retained audit\n * trail without that application ever opting in).\n */\n DISABLED = \"disabled\",\n /**\n * Minted per browsing session (`sessionStorage`) — dies when the tab closes and does NOT survive a\n * restart, so it cannot follow a user across visits. The proportionate choice when the goal is\n * correlating one session's activity; the easiest to justify as strictly necessary.\n */\n SESSION = \"session\",\n /**\n * Minted once per browser install (`localStorage`) — survives tab close, restart, logout, and\n * account switch. The strongest investigative signal (it links pre- and post-authentication\n * activity, and two accounts used from one machine) and correspondingly the most likely to require\n * consent. Select only with a recorded legitimate-interest assessment and, where required,\n * consent-gating.\n */\n PERSISTENT = \"persistent\",\n}\n\nexport const AnonymousUserPrivacySchema = z.object({\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC),\n});\n\nexport type AnonymousUserPrivacy = z.infer<typeof AnonymousUserPrivacySchema>;\n\nexport const InboundContactPrivacySchema = z.object({\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC),\n});\n\nexport type InboundContactPrivacy = z.infer<typeof InboundContactPrivacySchema>;\n\nexport const UserPrivacySchema = z.object({\n // `.isAuditEvidence()` on these two, and deliberately NOT on the block that contains them.\n //\n // Both are the subject's own privacy DECISIONS — the lawful basis for processing and the\n // collection scope — held as closed vocabularies, so recording their before/after values on\n // `resource_operation_performed` is exactly the \"mark what decides authority, never what\n // identifies a person\" rule that governs the same decorator on `UserSchema`.\n //\n // Evidence is selected per leaf; the audit walker resolves the dotted path. Governed-document\n // interactions are separate immutable release-bound records and are never embedded in preferences.\n //\n // assurance-control: WILDO.PRIVACY.CONSENT_RECORD — a consent change is a security-relevant\n // decision and must leave a trail naming the previous and resulting value. Reached today by the\n // preferences RESET, the generic UPDATE, and `update_privacy`, all of which route through the\n // same emission point, so no verb can change consent without a record.\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC).isAuditEvidence(),\n privacyLevel: z.enum(PrivacyLevel).default(PrivacyLevel.STANDARD).isAuditEvidence(),\n});\n\nexport type UserPrivacy = z.infer<typeof UserPrivacySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"privacy.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/privacy.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAN,IAAY,qBAKX;AALD,WAAY,qBAAqB;IAC/B,0CAAiB,CAAA;IACjB,sEAA6C,CAAA;IAC7C,sDAA6B,CAAA;IAC7B,4CAAmB,CAAA;AACrB,CAAC,EALW,qBAAqB,KAArB,qBAAqB,QAKhC;AAED,MAAM,CAAN,IAAY,6BAIX;AAJD,WAAY,6BAA6B;IACvC,kDAAiB,CAAA;IACjB,8EAA6C,CAAA;IAC7C,oDAAmB,CAAA;AACrB,CAAC,EAJW,6BAA6B,KAA7B,6BAA6B,QAIxC;AAED,MAAM,CAAN,IAAY,qBAMX;AAND,WAAY,qBAAqB;IAC/B,sCAAa,CAAA;IACb,wCAAe,CAAA;IACf,gDAAuB,CAAA;IACvB,gDAAuB,CAAA;IACvB,sCAAa,CAAA;AACf,CAAC,EANW,qBAAqB,KAArB,qBAAqB,QAMhC;AAED,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,mCAAmB,CAAA;IACnB,qCAAqB,CAAA;IACrB,qCAAqB,CAAA;AACvB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAN,IAAY,qBAsBX;AAtBD,WAAY,qBAAqB;IAC/B;;;;;OAKG;IACH,8CAAqB,CAAA;IACrB;;;;OAIG;IACH,4CAAmB,CAAA;IACnB;;;;;;OAMG;IACH,kDAAyB,CAAA;AAC3B,CAAC,EAtBW,qBAAqB,KAArB,qBAAqB,QAsBhC;AACD,0EAA0E;AAE1E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC;SACtF,QAAQ,CACP,6LAA6L,CAC9L;CACJ,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC;SACtF,QAAQ,CACP,gLAAgL,CACjL;CACJ,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,2FAA2F;IAC3F,EAAE;IACF,yFAAyF;IACzF,4FAA4F;IAC5F,yFAAyF;IACzF,6EAA6E;IAC7E,EAAE;IACF,8FAA8F;IAC9F,mGAAmG;IACnG,EAAE;IACF,4FAA4F;IAC5F,gGAAgG;IAChG,8FAA8F;IAC9F,uEAAuE;IACvE,qBAAqB,EAAE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC;SACtF,QAAQ,CACP,4SAA4S,CAC7S;SACA,eAAe,EAAE;IACpB,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC;SAC9D,QAAQ,CAAC,4JAA4J,CAAC;SACtK,eAAe,EAAE;CACrB,CAAC,CAAC","sourcesContent":["/**\n * Privacy Shared Schemas\n *\n * Privacy-related schemas and enums for user data management\n */\n\nimport { z } from \"zod\";\n\n/**\n * Profile Visibility Enum\n *\n * Controls who can see a user's profile information\n */\nexport enum UserProfileVisibility {\n PUBLIC = \"public\", // Visible to everyone\n APPLICATION_INTERNAL = \"application_internal\", // Visible within application, for connected users only\n ORGANIZATION = \"organization\", // Visible within organization only\n PRIVATE = \"private\", // Visible to user only\n}\n\nexport enum OrganizationProfileVisibility {\n PUBLIC = \"public\", // Visible to everyone\n APPLICATION_INTERNAL = \"application_internal\", // Visible within application, for connected users only\n PRIVATE = \"private\", // Visible to user only\n}\n\nexport enum DataProcessingConsent {\n NONE = \"none\",\n BASIC = \"basic\",\n ANALYTICS = \"analytics\",\n MARKETING = \"marketing\",\n FULL = \"full\",\n}\n\nexport enum PrivacyLevel {\n MINIMAL = \"minimal\", // Minimum data collection\n STANDARD = \"standard\", // Standard data collection\n ENHANCED = \"enhanced\", // Enhanced features with more data\n}\n\n/**\n * Whether — and with what lifetime — this application identifies the CLIENT INSTALL\n * (`clientInstanceId`, sent as `x-client-instance-id` and recorded on the audit row).\n *\n * A `clientInstanceId` answers \"which browser/device install issued this request?\", which the\n * per-request `correlationId` (server-minted, never stored on the device) and `frontendServiceName`\n * (a static per-app build constant) deliberately cannot. It is what lets an investigator group a\n * hostile actor's requests across sessions and logins — genuine security value, and exactly why it\n * needs a policy rather than a default.\n *\n * **This is a closed vocabulary with the OFF state inside it, on purpose.** Modelling it as\n * `{ enabled: boolean; scope: … }` would admit the meaningless `enabled: false, scope: PERSISTENT`\n * state; one enum makes \"off\" and \"how long it lives\" a single, always-coherent decision.\n *\n * **Why the framework refuses to choose for you (Fork D / SD9).** Storing an identifier on the\n * user's terminal equipment engages ePrivacy Directive Art 5(3), whose \"strictly necessary\"\n * exemption the EDPB reads narrowly (Guidelines 2/2023) — and *duration* is itself a\n * misclassification signal, so a long-lived id is the hardest case. GDPR Recital 49 (network and\n * information security as a legitimate interest) is a lawful basis for the PROCESSING but does not\n * remove the ePrivacy STORAGE consent question. Meanwhile NIST SP 800-53 AU-3 / AU-6(3)\n * affirmatively want this correlation capability for audit review. Those pull in opposite\n * directions, and the resolution is jurisdiction- and DPA-specific — so Wildo ships the mechanism\n * OFF and each application decides, with counsel, which member it may lawfully select.\n *\n * Not legal advice. See `.claude/plans/traceability-correlation-id.md` (SD9) for the full analysis\n * and the compliance obligations that attach to each non-`DISABLED` member.\n */\n/**\n * @wildo_source:part:start saas.models.privacy.client-instance-id-scope facet:layer:shared facet:audience:app-developer\n *\n * Whether the application identifies a browser INSTALL across visits, declared as\n * `compliance.clientInstanceIdScope`.\n *\n * It lives on `compliance` rather than beside the observability settings because it is a PRIVACY\n * decision, and on the SHARED config because both halves must obey one authored value: the frontend\n * decides whether to mint, store and send the id, and the backend independently decides whether to\n * ingest and record it. Two gates, one source — so an application that never opts in is protected\n * even if some client sends the header anyway.\n *\n * **`DISABLED` is the default**, so the privacy-preserving posture is what you get by doing nothing.\n * Choosing another member is choosing an obligation, not just a signal: start-up prints the\n * obligations each opt-in creates.\n */\nexport enum ClientInstanceIdScope {\n /**\n * DEFAULT. No client-install identifier is minted, sent, stored, or recorded — and the backend\n * additionally REFUSES an inbound `x-client-instance-id` (fail-closed, so a rogue or misconfigured\n * client cannot start populating a pseudonymous identifier in an application's retained audit\n * trail without that application ever opting in).\n */\n DISABLED = \"disabled\",\n /**\n * Minted per browsing session (`sessionStorage`) — dies when the tab closes and does NOT survive a\n * restart, so it cannot follow a user across visits. The proportionate choice when the goal is\n * correlating one session's activity; the easiest to justify as strictly necessary.\n */\n SESSION = \"session\",\n /**\n * Minted once per browser install (`localStorage`) — survives tab close, restart, logout, and\n * account switch. The strongest investigative signal (it links pre- and post-authentication\n * activity, and two accounts used from one machine) and correspondingly the most likely to require\n * consent. Select only with a recorded legitimate-interest assessment and, where required,\n * consent-gating.\n */\n PERSISTENT = \"persistent\",\n}\n/** @wildo_source:part:end saas.models.privacy.client-instance-id-scope */\n\nexport const AnonymousUserPrivacySchema = z.object({\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)\n .describe(\n 'The processing this anonymous visitor has agreed to. The application decides what each level permits — the framework stores the decision, it does not attach a fixed meaning to the levels.',\n ),\n});\n\nexport type AnonymousUserPrivacy = z.infer<typeof AnonymousUserPrivacySchema>;\n\nexport const InboundContactPrivacySchema = z.object({\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)\n .describe(\n 'The processing this lead has agreed to. The application decides what each level permits — the framework stores the decision, it does not attach a fixed meaning to the levels.',\n ),\n});\n\nexport type InboundContactPrivacy = z.infer<typeof InboundContactPrivacySchema>;\n\nexport const UserPrivacySchema = z.object({\n // `.isAuditEvidence()` on these two, and deliberately NOT on the block that contains them.\n //\n // Both are the subject's own privacy DECISIONS — the lawful basis for processing and the\n // collection scope — held as closed vocabularies, so recording their before/after values on\n // `resource_operation_performed` is exactly the \"mark what decides authority, never what\n // identifies a person\" rule that governs the same decorator on `UserSchema`.\n //\n // Evidence is selected per leaf; the audit walker resolves the dotted path. Governed-document\n // interactions are separate immutable release-bound records and are never embedded in preferences.\n //\n // assurance-control: WILDO.PRIVACY.CONSENT_RECORD — a consent change is a security-relevant\n // decision and must leave a trail naming the previous and resulting value. Reached today by the\n // preferences RESET, the generic UPDATE, and `update_privacy`, all of which route through the\n // same emission point, so no verb can change consent without a record.\n dataProcessingConsent: z.enum(DataProcessingConsent).default(DataProcessingConsent.BASIC)\n .describe(\n 'The processing this person has agreed to. It is the subject\\'s own decision and only they (or an administrator acting for them) change it. The application decides what each level permits — the framework stores the decision and records every change, it does not attach a fixed meaning to the levels.',\n )\n .isAuditEvidence(),\n privacyLevel: z.enum(PrivacyLevel).default(PrivacyLevel.STANDARD)\n .describe('How much this person has agreed the application may collect about them, alongside the processing consent. The application decides what each level permits.')\n .isAuditEvidence(),\n});\n\nexport type UserPrivacy = z.infer<typeof UserPrivacySchema>;\n"]}
|
|
@@ -1,48 +1,101 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
3
|
+
* @wildo_source:part:start saas.models.privacy.redaction-treatments facet:layer:shared facet:audience:app-developer
|
|
4
|
+
*
|
|
5
|
+
* How one field's value is replaced when it is redacted or scrubbed.
|
|
6
|
+
*
|
|
7
|
+
* Imported from `@wildo-ai/saas-models`.
|
|
8
|
+
*
|
|
9
|
+
* ## Two consumers, and only one of them accepts every member
|
|
10
|
+
*
|
|
11
|
+
* | consumer | what it does | accepts |
|
|
12
|
+
* |---|---|---|
|
|
13
|
+
* | flow-actors `redactionRules` | hides a value in a LOG or an API RESPONSE; the stored row is untouched | every member |
|
|
14
|
+
* | `.impersonalizeWith(...)` on a schema field | ERASURE scrub: permanently rewrites the stored value when the person is erased and the row is retained | `REMOVE` · `MASK` · `RESET` · `KEEP` only |
|
|
15
|
+
*
|
|
16
|
+
* **`HASH` and `ENCRYPT` are accepted by the type and REFUSED at erasure time.** An erasure scrub is
|
|
17
|
+
* one static write shared by every row in the batch, so `HASH` — which would have to hash each row's
|
|
18
|
+
* own value — cannot be expressed and throws. `ENCRYPT` is reversible, and an erasure that can be
|
|
19
|
+
* undone is not an erasure, so it is forbidden here and stays a redaction-only option. Both throw
|
|
20
|
+
* when the erasure runs, not when you author the field, so choosing one leaves a resource that
|
|
21
|
+
* configures cleanly and fails the first time somebody is erased.
|
|
22
|
+
*
|
|
23
|
+
* ## Choosing a treatment for an erasure scrub
|
|
24
|
+
*
|
|
25
|
+
* | the field | treatment |
|
|
26
|
+
* |---|---|
|
|
27
|
+
* | nullable, or `.nullish()` | `REMOVE` — writes `null`. The default choice. |
|
|
28
|
+
* | a constrained string that cannot hold the default mask (`z.email()`, `z.uuid()`, `.min(n)`) | `MASK` with an explicit `maskValue` that satisfies the field's own schema |
|
|
29
|
+
* | NON-nullable and carries a `.default()` — a counter, a settings object, a stage history, an array | `RESET` — writes what the schema says the field is when nobody has set it |
|
|
30
|
+
* | the row's link to its tenant or its creator | nothing: the never-scrub floor already protects it |
|
|
31
|
+
*
|
|
32
|
+
* `RESET` is the one to reach for when the other two do not fit, and that case is common rather than
|
|
33
|
+
* exotic: `REMOVE` writes `null`, which fails the strict re-parse of the row the erasure just wrote,
|
|
34
|
+
* and `MASK` writes a string, which cannot express an array, a record, a number or a date.
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ **`RESET` refuses a default that reads the clock.** `.default(() => new Date())` — or a literal
|
|
37
|
+
* `.default(new Date())` evaluated when the module loads — makes the erasure write its OWN timestamp,
|
|
38
|
+
* so a field such as `lastActiveAt` would record the erased person as active at the moment they were
|
|
39
|
+
* erased. That fabricates information rather than removing it. Give the field a static neutral
|
|
40
|
+
* default such as `new Date(0)`, which asserts "no recorded value", or make it nullable and use
|
|
41
|
+
* `REMOVE`, which asserts "unknown". Those are different claims about a person; choose deliberately.
|
|
42
|
+
*
|
|
43
|
+
* ## The never-scrub floor
|
|
44
|
+
*
|
|
45
|
+
* A row's link to the organization or application it belongs to, and its `createdByUserId`, are
|
|
46
|
+
* never scrubbed — even when a treatment is declared on them. They connect the retained row to the
|
|
47
|
+
* tenant that must keep it, and nulling them would orphan the record the retention exists to
|
|
48
|
+
* preserve. `KEEP` is the way to say the same thing explicitly about any other field.
|
|
49
|
+
*
|
|
50
|
+
* A field with NO `.impersonalizeWith()` is left untouched by an erasure. Declaring nothing is how a
|
|
51
|
+
* legally required value — an invoice line, an amount, a date — survives the erasure of the person
|
|
52
|
+
* the record names.
|
|
53
|
+
*
|
|
54
|
+
* @example Scrubbing a person out of a retained record
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { RedactionType } from '@wildo-ai/saas-models';
|
|
57
|
+
*
|
|
58
|
+
* const contactSchema = z.object({
|
|
59
|
+
* // nullable → REMOVE
|
|
60
|
+
* phone: z.string().nullish().impersonalizeWith(RedactionType.REMOVE),
|
|
61
|
+
* // constrained string → MASK with a value the field's own schema accepts
|
|
62
|
+
* email: z.email().impersonalizeWith(RedactionType.MASK, { maskValue: 'erased@erased.invalid' }),
|
|
63
|
+
* // non-nullable with a default → RESET
|
|
64
|
+
* preferences: z.object({ digest: z.boolean() }).default({ digest: false })
|
|
65
|
+
* .impersonalizeWith(RedactionType.RESET),
|
|
66
|
+
* // no treatment → survives the erasure, deliberately
|
|
67
|
+
* invoicedAmount: z.number(),
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
13
70
|
*/
|
|
14
71
|
export declare enum RedactionType {
|
|
15
|
-
/** Replace with a fixed mask
|
|
72
|
+
/** Replace with a fixed mask. In an erasure scrub the value must satisfy the field's own schema — pass `maskValue` when the default mask does not. */
|
|
16
73
|
MASK = "mask",
|
|
17
|
-
/** Remove / null the value.
|
|
74
|
+
/** Remove / null the value. The default erasure scrub; requires a field that can actually hold `null`. */
|
|
18
75
|
REMOVE = "remove",
|
|
19
|
-
/** Replace with a one-way hash —
|
|
76
|
+
/** Replace with a one-way hash. Redaction only — **throws** if an erasure scrub reaches it, because a scrub is one static write and cannot hash per row. */
|
|
20
77
|
HASH = "hash",
|
|
21
|
-
/** Replace with a reversible ciphertext
|
|
78
|
+
/** Replace with a reversible ciphertext. Redaction only — **forbidden** for an erasure scrub, because a reversible erasure is not an erasure. */
|
|
22
79
|
ENCRYPT = "encrypt",
|
|
23
|
-
/**
|
|
80
|
+
/** Erasure scrub only: keep this field exactly as it is. The explicit form of the never-scrub floor. */
|
|
24
81
|
KEEP = "keep",
|
|
25
82
|
/**
|
|
26
|
-
*
|
|
83
|
+
* Erasure scrub only: reset the field to its OWN schema default.
|
|
27
84
|
*
|
|
28
|
-
* The treatment for a field that is non-nullable and carries a `.default()
|
|
29
|
-
*
|
|
30
|
-
* wrote
|
|
31
|
-
*
|
|
85
|
+
* The treatment for a field that is non-nullable and carries a `.default()`, where neither of the
|
|
86
|
+
* others reaches: `REMOVE` writes `null`, which fails the strict re-parse of the row the erasure
|
|
87
|
+
* just wrote, and `MASK` writes a string, while these fields are arrays, records, numbers, dates
|
|
88
|
+
* and objects.
|
|
32
89
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* could declare no honest posture at all: `RETAIN_AND_IMPERSONALIZE` is refused when nothing would
|
|
37
|
-
* be scrubbed, and `RETAIN_ONLY` would assert they hold no personal data, which is false.
|
|
90
|
+
* The value is read from the schema rather than authored a second time, so it cannot drift from
|
|
91
|
+
* the default the field already declares — and for a person's counters and settings, "what this is
|
|
92
|
+
* when nobody has set it" is exactly what an erasure should leave behind.
|
|
38
93
|
*
|
|
39
|
-
*
|
|
40
|
-
* default would drift from the first, and "what this field is when nobody has set it" is a question
|
|
41
|
-
* the schema already answers — for a person's settings and counters that is exactly what erasure
|
|
42
|
-
* should leave behind.
|
|
94
|
+
* Refused when the field declares no `.default()`, and refused when that default reads the clock.
|
|
43
95
|
*/
|
|
44
96
|
RESET = "reset"
|
|
45
97
|
}
|
|
98
|
+
/** @wildo_source:part:end saas.models.privacy.redaction-treatments */
|
|
46
99
|
export declare const RedationRulesSchema: z.ZodObject<{
|
|
47
100
|
fieldPath: z.ZodString;
|
|
48
101
|
isRecursive: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redaction.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/redaction.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"redaction.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/redaction.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,oBAAY,aAAa;IACvB,sJAAsJ;IACtJ,IAAI,SAAS;IACb,0GAA0G;IAC1G,MAAM,WAAW;IACjB,4JAA4J;IAC5J,IAAI,SAAS;IACb,iJAAiJ;IACjJ,OAAO,YAAY;IACnB,wGAAwG;IACxG,IAAI,SAAS;IACb;;;;;;;;;;;;;OAaG;IACH,KAAK,UAAU;CAChB;AACD,sEAAsE;AAGtE,eAAO,MAAM,mBAAmB;;;;;iBAK9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,50 +1,116 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { RolesSchema } from "../../security/authorizations/roles.shared.schemas.js";
|
|
3
|
+
/*
|
|
4
|
+
* MAINTAINER NOTE — not part of the anchored, application-facing block below.
|
|
5
|
+
*
|
|
6
|
+
* One authority axis (D9): this vocabulary is shared by flow-actors `redactionRules` and by the
|
|
7
|
+
* impersonalization `.impersonalizeWith()` treatment, deliberately, so the two cannot drift into two
|
|
8
|
+
* spellings of the same idea. Adding a member is additive for both consumers — no exhaustive switch
|
|
9
|
+
* reads it — EXCEPT `buildImpersonalizeScrubPatch`, whose `default` arm throws on purpose so a new
|
|
10
|
+
* member fails loud in an erasure rather than being silently skipped.
|
|
11
|
+
*
|
|
12
|
+
* Reversible tokenization is intentionally NOT in v1 (D6 = irreversible). `ENCRYPT` stays the
|
|
13
|
+
* existing reversible option for redaction, and a vault-backed `TOKENIZE` member can be added
|
|
14
|
+
* additively if such a mode ships.
|
|
15
|
+
*/
|
|
3
16
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* @wildo_source:part:start saas.models.privacy.redaction-treatments facet:layer:shared facet:audience:app-developer
|
|
18
|
+
*
|
|
19
|
+
* How one field's value is replaced when it is redacted or scrubbed.
|
|
20
|
+
*
|
|
21
|
+
* Imported from `@wildo-ai/saas-models`.
|
|
22
|
+
*
|
|
23
|
+
* ## Two consumers, and only one of them accepts every member
|
|
24
|
+
*
|
|
25
|
+
* | consumer | what it does | accepts |
|
|
26
|
+
* |---|---|---|
|
|
27
|
+
* | flow-actors `redactionRules` | hides a value in a LOG or an API RESPONSE; the stored row is untouched | every member |
|
|
28
|
+
* | `.impersonalizeWith(...)` on a schema field | ERASURE scrub: permanently rewrites the stored value when the person is erased and the row is retained | `REMOVE` · `MASK` · `RESET` · `KEEP` only |
|
|
29
|
+
*
|
|
30
|
+
* **`HASH` and `ENCRYPT` are accepted by the type and REFUSED at erasure time.** An erasure scrub is
|
|
31
|
+
* one static write shared by every row in the batch, so `HASH` — which would have to hash each row's
|
|
32
|
+
* own value — cannot be expressed and throws. `ENCRYPT` is reversible, and an erasure that can be
|
|
33
|
+
* undone is not an erasure, so it is forbidden here and stays a redaction-only option. Both throw
|
|
34
|
+
* when the erasure runs, not when you author the field, so choosing one leaves a resource that
|
|
35
|
+
* configures cleanly and fails the first time somebody is erased.
|
|
36
|
+
*
|
|
37
|
+
* ## Choosing a treatment for an erasure scrub
|
|
38
|
+
*
|
|
39
|
+
* | the field | treatment |
|
|
40
|
+
* |---|---|
|
|
41
|
+
* | nullable, or `.nullish()` | `REMOVE` — writes `null`. The default choice. |
|
|
42
|
+
* | a constrained string that cannot hold the default mask (`z.email()`, `z.uuid()`, `.min(n)`) | `MASK` with an explicit `maskValue` that satisfies the field's own schema |
|
|
43
|
+
* | NON-nullable and carries a `.default()` — a counter, a settings object, a stage history, an array | `RESET` — writes what the schema says the field is when nobody has set it |
|
|
44
|
+
* | the row's link to its tenant or its creator | nothing: the never-scrub floor already protects it |
|
|
45
|
+
*
|
|
46
|
+
* `RESET` is the one to reach for when the other two do not fit, and that case is common rather than
|
|
47
|
+
* exotic: `REMOVE` writes `null`, which fails the strict re-parse of the row the erasure just wrote,
|
|
48
|
+
* and `MASK` writes a string, which cannot express an array, a record, a number or a date.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ **`RESET` refuses a default that reads the clock.** `.default(() => new Date())` — or a literal
|
|
51
|
+
* `.default(new Date())` evaluated when the module loads — makes the erasure write its OWN timestamp,
|
|
52
|
+
* so a field such as `lastActiveAt` would record the erased person as active at the moment they were
|
|
53
|
+
* erased. That fabricates information rather than removing it. Give the field a static neutral
|
|
54
|
+
* default such as `new Date(0)`, which asserts "no recorded value", or make it nullable and use
|
|
55
|
+
* `REMOVE`, which asserts "unknown". Those are different claims about a person; choose deliberately.
|
|
56
|
+
*
|
|
57
|
+
* ## The never-scrub floor
|
|
58
|
+
*
|
|
59
|
+
* A row's link to the organization or application it belongs to, and its `createdByUserId`, are
|
|
60
|
+
* never scrubbed — even when a treatment is declared on them. They connect the retained row to the
|
|
61
|
+
* tenant that must keep it, and nulling them would orphan the record the retention exists to
|
|
62
|
+
* preserve. `KEEP` is the way to say the same thing explicitly about any other field.
|
|
63
|
+
*
|
|
64
|
+
* A field with NO `.impersonalizeWith()` is left untouched by an erasure. Declaring nothing is how a
|
|
65
|
+
* legally required value — an invoice line, an amount, a date — survives the erasure of the person
|
|
66
|
+
* the record names.
|
|
67
|
+
*
|
|
68
|
+
* @example Scrubbing a person out of a retained record
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { RedactionType } from '@wildo-ai/saas-models';
|
|
71
|
+
*
|
|
72
|
+
* const contactSchema = z.object({
|
|
73
|
+
* // nullable → REMOVE
|
|
74
|
+
* phone: z.string().nullish().impersonalizeWith(RedactionType.REMOVE),
|
|
75
|
+
* // constrained string → MASK with a value the field's own schema accepts
|
|
76
|
+
* email: z.email().impersonalizeWith(RedactionType.MASK, { maskValue: 'erased@erased.invalid' }),
|
|
77
|
+
* // non-nullable with a default → RESET
|
|
78
|
+
* preferences: z.object({ digest: z.boolean() }).default({ digest: false })
|
|
79
|
+
* .impersonalizeWith(RedactionType.RESET),
|
|
80
|
+
* // no treatment → survives the erasure, deliberately
|
|
81
|
+
* invoicedAmount: z.number(),
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
14
84
|
*/
|
|
15
85
|
export var RedactionType;
|
|
16
86
|
(function (RedactionType) {
|
|
17
|
-
/** Replace with a fixed mask
|
|
87
|
+
/** Replace with a fixed mask. In an erasure scrub the value must satisfy the field's own schema — pass `maskValue` when the default mask does not. */
|
|
18
88
|
RedactionType["MASK"] = "mask";
|
|
19
|
-
/** Remove / null the value.
|
|
89
|
+
/** Remove / null the value. The default erasure scrub; requires a field that can actually hold `null`. */
|
|
20
90
|
RedactionType["REMOVE"] = "remove";
|
|
21
|
-
/** Replace with a one-way hash —
|
|
91
|
+
/** Replace with a one-way hash. Redaction only — **throws** if an erasure scrub reaches it, because a scrub is one static write and cannot hash per row. */
|
|
22
92
|
RedactionType["HASH"] = "hash";
|
|
23
|
-
/** Replace with a reversible ciphertext
|
|
93
|
+
/** Replace with a reversible ciphertext. Redaction only — **forbidden** for an erasure scrub, because a reversible erasure is not an erasure. */
|
|
24
94
|
RedactionType["ENCRYPT"] = "encrypt";
|
|
25
|
-
/**
|
|
95
|
+
/** Erasure scrub only: keep this field exactly as it is. The explicit form of the never-scrub floor. */
|
|
26
96
|
RedactionType["KEEP"] = "keep";
|
|
27
97
|
/**
|
|
28
|
-
*
|
|
98
|
+
* Erasure scrub only: reset the field to its OWN schema default.
|
|
29
99
|
*
|
|
30
|
-
* The treatment for a field that is non-nullable and carries a `.default()
|
|
31
|
-
*
|
|
32
|
-
* wrote
|
|
33
|
-
*
|
|
100
|
+
* The treatment for a field that is non-nullable and carries a `.default()`, where neither of the
|
|
101
|
+
* others reaches: `REMOVE` writes `null`, which fails the strict re-parse of the row the erasure
|
|
102
|
+
* just wrote, and `MASK` writes a string, while these fields are arrays, records, numbers, dates
|
|
103
|
+
* and objects.
|
|
34
104
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* could declare no honest posture at all: `RETAIN_AND_IMPERSONALIZE` is refused when nothing would
|
|
39
|
-
* be scrubbed, and `RETAIN_ONLY` would assert they hold no personal data, which is false.
|
|
105
|
+
* The value is read from the schema rather than authored a second time, so it cannot drift from
|
|
106
|
+
* the default the field already declares — and for a person's counters and settings, "what this is
|
|
107
|
+
* when nobody has set it" is exactly what an erasure should leave behind.
|
|
40
108
|
*
|
|
41
|
-
*
|
|
42
|
-
* default would drift from the first, and "what this field is when nobody has set it" is a question
|
|
43
|
-
* the schema already answers — for a person's settings and counters that is exactly what erasure
|
|
44
|
-
* should leave behind.
|
|
109
|
+
* Refused when the field declares no `.default()`, and refused when that default reads the clock.
|
|
45
110
|
*/
|
|
46
111
|
RedactionType["RESET"] = "reset";
|
|
47
112
|
})(RedactionType || (RedactionType = {}));
|
|
113
|
+
/** @wildo_source:part:end saas.models.privacy.redaction-treatments */
|
|
48
114
|
export const RedationRulesSchema = z.object({
|
|
49
115
|
fieldPath: z.string(),
|
|
50
116
|
isRecursive: z.boolean().default(false), // default false - if true, will redaction all nested fields
|