@wildo-ai/saas-models 1.1.8 → 1.1.9
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/companion/provider-discovery.shared.schemas.d.ts +13 -0
- package/dist/esm/companion/provider-discovery.shared.schemas.d.ts.map +1 -1
- package/dist/esm/companion/provider-discovery.shared.schemas.js +13 -0
- package/dist/esm/companion/provider-discovery.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts +32 -1
- package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js +32 -0
- package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js.map +1 -1
- package/dist/esm/compliance/risk-posture.shared.schemas.d.ts +15 -3
- package/dist/esm/compliance/risk-posture.shared.schemas.d.ts.map +1 -1
- package/dist/esm/compliance/risk-posture.shared.schemas.js +14 -2
- package/dist/esm/compliance/risk-posture.shared.schemas.js.map +1 -1
- package/dist/esm/meta-workflows/elicitation-depth.meta-workflows.shared.schemas.d.ts +128 -0
- package/dist/esm/meta-workflows/elicitation-depth.meta-workflows.shared.schemas.d.ts.map +1 -0
- package/dist/esm/meta-workflows/elicitation-depth.meta-workflows.shared.schemas.js +136 -0
- package/dist/esm/meta-workflows/elicitation-depth.meta-workflows.shared.schemas.js.map +1 -0
- package/dist/esm/meta-workflows/orchestration.meta-workflows.shared.schemas.d.ts +32 -0
- package/dist/esm/meta-workflows/orchestration.meta-workflows.shared.schemas.d.ts.map +1 -1
- package/dist/esm/meta-workflows/orchestration.meta-workflows.shared.schemas.js +66 -1
- package/dist/esm/meta-workflows/orchestration.meta-workflows.shared.schemas.js.map +1 -1
- package/dist/esm/meta-workflows.exports.d.ts +1 -0
- package/dist/esm/meta-workflows.exports.d.ts.map +1 -1
- package/dist/esm/meta-workflows.exports.js +1 -0
- package/dist/esm/meta-workflows.exports.js.map +1 -1
- package/dist/esm/queue/jobs.shared.schemas.d.ts +70 -1
- package/dist/esm/queue/jobs.shared.schemas.d.ts.map +1 -1
- package/dist/esm/queue/jobs.shared.schemas.js +85 -2
- package/dist/esm/queue/jobs.shared.schemas.js.map +1 -1
- package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.d.ts +25 -0
- package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.d.ts.map +1 -1
- package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js +47 -19
- package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js.map +1 -1
- package/package.json +4 -4
- package/dist/esm/__tests__/setup.d.ts +0 -2
- package/dist/esm/__tests__/setup.d.ts.map +0 -1
- package/dist/esm/__tests__/setup.js +0 -19
- package/dist/esm/__tests__/setup.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
|
@@ -132,6 +132,19 @@ export declare const ProviderCompanionContributionSchema: z.ZodObject<{
|
|
|
132
132
|
required: z.ZodBoolean;
|
|
133
133
|
description: z.ZodString;
|
|
134
134
|
}, z.core.$strict>>;
|
|
135
|
+
/**
|
|
136
|
+
* Per protocol, the environment variable its API key is read from — the DEFAULT `authConfig.secretRef`
|
|
137
|
+
* a running host derives from the binding's runtime contract, so the value `getProviderApiKey` reads
|
|
138
|
+
* before it refuses a call for a missing key (#2229). Keyed by the same protocol strings as `protocols`;
|
|
139
|
+
* a protocol absent from the map authenticates with nothing a host must hold.
|
|
140
|
+
*
|
|
141
|
+
* Carried so tooling can predict that refusal from discovery without loading a provider module — the
|
|
142
|
+
* creation preflight names the ONE key a step needs, where `secretsContractShape` lists every key the
|
|
143
|
+
* provider may use (Stripe's webhook secret is required by its shape and read by no boot refusal).
|
|
144
|
+
* Values-free, and optional on the wire because a contribution emitted before #2229 carries none; a
|
|
145
|
+
* reader then falls back to the required entries of `secretsContractShape`.
|
|
146
|
+
*/
|
|
147
|
+
protocolSecretRefs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
135
148
|
/**
|
|
136
149
|
* Optional frontend-safe provider payload for `frontendService` runtime
|
|
137
150
|
* targets. Tooling materializes this into generated runtime artifacts so the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-discovery.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/companion/provider-discovery.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0DAA0D,CAAC;AAE5F,OAAO,EAAgD,YAAY,EAAE,MAAM,wDAAwD,CAAC;AAGpI;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0CAA0C;;;kBAGrD,CAAC;AACH,MAAM,MAAM,oCAAoC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0CAA0C,CAAC,CAAC;AAE9G,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;4BAgB/C,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sCAAsC;;;;;kBAKjD,CAAC;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AAEtG;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC;;;;;mBA+B5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC;;IAE9C;;;;;;OAMG;;;;;;;;;;;;;;IAEH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;IAOH;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH;;;OAGG;;IAEH;;;;;;;;OAQG;;;;;;IAMH;;;;;;;OAOG;;IAEH;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;;;iBAEH,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,sCAAsC;IACjD,wGAAwG;;IAExG,0EAA0E;;IAE1E,yFAAyF;;;;;;;;;;IAGzF,iGAAiG;;IAEjG,mFAAmF;;IAEnF,gGAAgG;;iBAUhG,CAAC;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AAEtG,MAAM,WAAW,4BAA4B;IAC3C,qBAAqB,CAAC,EAAE,6BAA6B,EAAE,CAAC;IACxD;;;OAGG;IACH,qBAAqB,CAAC,EAAE,gCAAgC,EAAE,CAAC;CAC5D"}
|
|
1
|
+
{"version":3,"file":"provider-discovery.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/companion/provider-discovery.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0DAA0D,CAAC;AAE5F,OAAO,EAAgD,YAAY,EAAE,MAAM,wDAAwD,CAAC;AAGpI;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0CAA0C;;;kBAGrD,CAAC;AACH,MAAM,MAAM,oCAAoC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0CAA0C,CAAC,CAAC;AAE9G,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;4BAgB/C,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sCAAsC;;;;;kBAKjD,CAAC;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AAEtG;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC;;;;;mBA+B5C,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC;;IAE9C;;;;;;OAMG;;;;;;;;;;;;;;IAEH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;IAOH;;;;;;;;;;;OAWG;;IAEH;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH;;;OAGG;;IAEH;;;;;;;;OAQG;;;;;;IAMH;;;;;;;OAOG;;IAEH;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;;;iBAEH,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,sCAAsC;IACjD,wGAAwG;;IAExG,0EAA0E;;IAE1E,yFAAyF;;;;;;;;;;IAGzF,iGAAiG;;IAEjG,mFAAmF;;IAEnF,gGAAgG;;iBAUhG,CAAC;AACH,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AAEtG,MAAM,WAAW,4BAA4B;IAC3C,qBAAqB,CAAC,EAAE,6BAA6B,EAAE,CAAC;IACxD;;;OAGG;IACH,qBAAqB,CAAC,EAAE,gCAAgC,EAAE,CAAC;CAC5D"}
|
|
@@ -126,6 +126,19 @@ export const ProviderCompanionContributionSchema = z.looseObject({
|
|
|
126
126
|
providerCapabilities: z.array(z.string().min(1)).default([]),
|
|
127
127
|
protocols: z.array(z.string().min(1)).default([]),
|
|
128
128
|
secretsContractShape: ProviderSecretContractShapeSchema,
|
|
129
|
+
/**
|
|
130
|
+
* Per protocol, the environment variable its API key is read from — the DEFAULT `authConfig.secretRef`
|
|
131
|
+
* a running host derives from the binding's runtime contract, so the value `getProviderApiKey` reads
|
|
132
|
+
* before it refuses a call for a missing key (#2229). Keyed by the same protocol strings as `protocols`;
|
|
133
|
+
* a protocol absent from the map authenticates with nothing a host must hold.
|
|
134
|
+
*
|
|
135
|
+
* Carried so tooling can predict that refusal from discovery without loading a provider module — the
|
|
136
|
+
* creation preflight names the ONE key a step needs, where `secretsContractShape` lists every key the
|
|
137
|
+
* provider may use (Stripe's webhook secret is required by its shape and read by no boot refusal).
|
|
138
|
+
* Values-free, and optional on the wire because a contribution emitted before #2229 carries none; a
|
|
139
|
+
* reader then falls back to the required entries of `secretsContractShape`.
|
|
140
|
+
*/
|
|
141
|
+
protocolSecretRefs: z.record(z.string().min(1), z.string().min(1)).optional(),
|
|
129
142
|
/**
|
|
130
143
|
* Optional frontend-safe provider payload for `frontendService` runtime
|
|
131
144
|
* targets. Tooling materializes this into generated runtime artifacts so the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-discovery.shared.schemas.js","sourceRoot":"","sources":["../../../../src/companion/provider-discovery.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { AppFrontendType } from '../config/app-frontend-type.shared.schemas';\nimport { EngineCapability } from '../external-providers/engine-capabilities.shared.schemas';\nimport { FrontendProviderEntrySchema } from '../external-providers/frontend-bootstrap.shared.schemas';\nimport { ProviderIdentitySchema, ProviderOriginSchema, ProviderTier } from '../external-providers/provider-metadata.shared.schemas';\nimport { ProviderComplianceProfileSchema } from '../external-providers/provider-compliance-profile.shared.schemas';\n\n/**\n * Companion-safe provider implementation declaration.\n *\n * App/runtime-owner packages expose these entries through their `./companion`\n * default export so tooling can discover which provider refs are actually\n * reachable from which runtime owners before materializing generated artifacts.\n */\n\n/**\n * Discriminator branch for `frontendService` runtime targets.\n *\n * Targets a frontend service by its `AppFrontendType` (the closed kind set\n * — SAAS_APP, STATIC_WEBSITE, TECH_DOC) rather than by an app-defined\n * `serviceKey` string. Engine-shipped packages (`external-connectors-public`,\n * etc.) have no way to know app-defined service keys, so the discovery\n * contract is keyed on the type. Apps with multiple frontend services of\n * the same type currently broadcast the contribution to all of them; if\n * per-service-key targeting becomes a need, add a second variant.\n */\nexport const ProviderFrontendServiceRuntimeTargetSchema = z.strictObject({\n kind: z.literal('frontendService'),\n serviceType: z.enum(AppFrontendType),\n});\nexport type ProviderFrontendServiceRuntimeTarget = z.infer<typeof ProviderFrontendServiceRuntimeTargetSchema>;\n\nexport const ProviderCompanionRuntimeTargetSchema = z.discriminatedUnion('kind', [\n z.strictObject({\n kind: z.literal('backend'),\n }),\n z.strictObject({\n kind: z.literal('companion'),\n }),\n ProviderFrontendServiceRuntimeTargetSchema,\n z.strictObject({\n kind: z.literal('minion'),\n minionKey: z.string().min(1),\n }),\n z.strictObject({\n kind: z.literal('worker'),\n workerKey: z.string().min(1),\n }),\n]);\nexport type ProviderCompanionRuntimeTarget = z.infer<typeof ProviderCompanionRuntimeTargetSchema>;\n\n/**\n * Values-free projection of one backend provider secret requirement.\n *\n * The stable provider-owned `slug` is preserved separately from the concrete\n * environment-variable name so a rename on either axis changes the lockfile\n * integrity digest. Secret values never cross this companion-safe boundary.\n */\nexport const ProviderSecretContractShapeEntrySchema = z.strictObject({\n slug: z.string().min(1),\n envVarName: z.string().min(1),\n required: z.boolean(),\n description: z.string().min(1),\n});\nexport type ProviderSecretContractShapeEntry = z.infer<typeof ProviderSecretContractShapeEntrySchema>;\n\n/**\n * Deterministic, values-free secret-contract shape used by provider discovery\n * and lockfile integrity checks.\n *\n * Every contribution must declare this field. `[]` means the provider is\n * authoritatively known to require no secrets; omission is rejected rather\n * than being silently interpreted as an empty contract. Entries must be\n * sorted by `slug`, and both slugs and environment-variable names are unique,\n * so independently contributed facets cannot produce order-dependent digests.\n */\nexport const ProviderSecretContractShapeSchema = z.array(ProviderSecretContractShapeEntrySchema).superRefine((entries, context) => {\n const seenSlugs = new Set<string>();\n const seenEnvVarNames = new Set<string>();\n\n for (const [index, entry] of entries.entries()) {\n if (seenSlugs.has(entry.slug)) {\n context.addIssue({\n code: 'custom',\n path: [index, 'slug'],\n message: `Duplicate provider secret-contract slug \"${entry.slug}\".`,\n });\n }\n seenSlugs.add(entry.slug);\n\n if (seenEnvVarNames.has(entry.envVarName)) {\n context.addIssue({\n code: 'custom',\n path: [index, 'envVarName'],\n message: `Duplicate provider secret-contract envVarName \"${entry.envVarName}\".`,\n });\n }\n seenEnvVarNames.add(entry.envVarName);\n\n if (index > 0 && entries[index - 1].slug.localeCompare(entry.slug) >= 0) {\n context.addIssue({\n code: 'custom',\n path: [index, 'slug'],\n message: 'Provider secret-contract entries must be sorted by slug.',\n });\n }\n }\n});\nexport type ProviderSecretContractShape = z.infer<typeof ProviderSecretContractShapeSchema>;\n\n/**\n * A contribution is a wire record produced by one package version and read by\n * another (the CLI, the companion, a generated artifact), so it is a LOOSE\n * object: an additive field from a newer producer rides through an older\n * consumer instead of hard-failing the parse (#328 — it used to be strict,\n * which made every additive field a five-surface change and every old/new\n * mix a boot failure). Required fields stay required; only UNKNOWN keys are\n * tolerated, and they are kept rather than stripped so a re-emitting\n * consumer does not silently drop what it did not understand.\n */\nexport const ProviderCompanionContributionSchema = z.looseObject({\n ref: z.string().min(1),\n /**\n * The provider's identity (package, tier, vendor, origin) as declared on\n * its module metadata, so tooling can display tiers and group vendors\n * without loading the runtime module. Optional on the wire for one\n * reason only: contributions emitted before #328 carry none, and a\n * discovery walk must still read them.\n */\n identity: ProviderIdentitySchema.optional(),\n /**\n * Stable package import path that makes the provider implementation reachable\n * from the contributing package at runtime. This validates owner-package\n * reachability without coupling discovery to source-file paths.\n */\n runtimeImportPath: z.string().min(1),\n runtimeTargets: z.array(ProviderCompanionRuntimeTargetSchema).min(1),\n engineCapabilities: z.array(z.enum(EngineCapability)).default([]),\n providerCapabilities: z.array(z.string().min(1)).default([]),\n protocols: z.array(z.string().min(1)).default([]),\n secretsContractShape: ProviderSecretContractShapeSchema,\n /**\n * Optional frontend-safe provider payload for `frontendService` runtime\n * targets. Tooling materializes this into generated runtime artifacts so the\n * backend bootstrap endpoint can serve app-owned public providers without\n * importing frontend packages at runtime.\n */\n frontendEntry: FrontendProviderEntrySchema.optional(),\n /**\n * Optional app module owner when the provider implementation belongs to one\n * concrete module instead of the whole package/runtime.\n */\n sourceModuleId: z.string().min(1).optional(),\n /**\n * The hosts the provider's OAuth2 TOKEN endpoint may resolve to (#1725) — derived by the producer with\n * `describeProviderOAuth2TokenEndpointHosts`, absent when the provider runs no OAuth2 connected account.\n *\n * Carried so `wildo config sync` can refuse a declared shared connection whose member names a different\n * authorization server than its base, without loading either module. Values-free: a login host is not a\n * secret. Optional on the wire because a contribution emitted before #1725 carries none; the registry then\n * makes the same comparison on the loaded modules at boot.\n */\n oauth2TokenEndpointHosts: z.strictObject({\n exact: z.array(z.string().min(1)),\n suffixes: z.array(z.string().min(1)),\n undeterminable: z.boolean(),\n }).optional(),\n /**\n * The secrets-contract SLUGS holding the provider's connected-account OAuth2 client registration — its client id\n * and client secret (#1725). Absent when the provider runs no OAuth2 connected account.\n *\n * Carried so `wildo secrets` can tell an operator which registration a declared shared connection actually needs:\n * the BASE's slots are then required, and a MEMBER's own slots are inert — reported, never required — because the\n * base's client connects and refreshes the one grant.\n */\n oauth2ClientSlotSlugs: z.array(z.string().min(1)).optional(),\n /**\n * What the provider's BACKEND module declares about who runs it and where it processes (#1708) — ESTABLISHED\n * facts, or UNDETERMINED. A frontend facet's profile rides `frontendEntry.complianceProfile` instead (#1743).\n *\n * Carried so the compliance readers (the evidence collector, the companion's drafting brief) learn every\n * declared provider's profile from discovery, WITHOUT loading a provider module — the same reason\n * `oauth2TokenEndpointHosts` is here. Values-free: a vendor's region list is not a secret. Optional on the wire\n * because a contribution emitted before #1708 carries none, which a reader reports as \"no profile\".\n */\n complianceProfile: ProviderComplianceProfileSchema.optional(),\n});\nexport type ProviderCompanionContribution = z.infer<typeof ProviderCompanionContributionSchema>;\n\n/**\n * A package's declaration that it ADDS OPERATIONS to a provider it does not own (#1672).\n *\n * ## Why this is its own record and not a `ProviderCompanionContribution`\n *\n * A contribution says \"this package OWNS provider X\": identity, secrets contract, protocols, the runtimes\n * it reaches. Discovery keeps one owner per ref and compares every contribution's secrets-contract shape\n * against the others. An augmentation owns none of that — the base keeps identity, tier, secrets contract,\n * OAuth2 block and connection — so recording it as a second contribution of the same ref would make the\n * shape comparison treat it as a second owner, and the import map would pick one of the two by sort order.\n * A separate record is what lets the base stay the base.\n *\n * ## What it carries, and why each field is STATIC\n *\n * `operationIds` and `requiredScopes` are stated here rather than read from the catalogue because a\n * catalogue is a LAZY loader that tooling must never call (`ProviderRestOperationCatalogueLoader`). So\n * `wildo config sync` can refuse an operation-id collision and record which scopes an augmentation may ask\n * for without evaluating any vendor code; the runtime then checks that what the loader delivers is exactly\n * what was declared here, and refuses the augmentation otherwise.\n *\n * INERT until an application opts it in under `providers.augmentations.<augments>` — installing a package\n * never adds an operation to a provider on its own.\n *\n * Loose for the reason `ProviderCompanionContributionSchema` is: a newer producer's additive field must ride\n * through an older consumer.\n */\nexport const ProviderAugmentationContributionSchema = z.looseObject({\n /** The ref of the provider this package adds operations to — owned by the engine or another package. */\n augments: z.string().min(1),\n /** The augmentation package's own npm name, as its manifest states it. */\n packageName: z.string().min(1),\n /** Always `CATALOGUE`: an augmentation binds no engine slot and adds only operations. */\n tier: z.literal(ProviderTier.CATALOGUE),\n origin: ProviderOriginSchema,\n /** The specifier a host imports to read the augmentation (`<package>/backend` by convention). */\n runtimeImportPath: z.string().min(1),\n /** Every operation id the augmentation contributes — sorted, unique, non-empty. */\n operationIds: z.array(z.string().min(1)).min(1),\n /** Every vendor scope any of those operations declares in `requiredScopes` — sorted, unique. */\n requiredScopes: z.array(z.string().min(1)).default([]),\n}).superRefine((contribution, ctx) => {\n for (const [field, values] of [['operationIds', contribution.operationIds], ['requiredScopes', contribution.requiredScopes]] as const) {\n for (let index = 1; index < values.length; index += 1) {\n if (values[index - 1].localeCompare(values[index]) >= 0) {\n ctx.addIssue({ code: 'custom', path: [field, index], message: `${field} must be sorted and unique (\"${values[index]}\" follows \"${values[index - 1]}\").` });\n }\n }\n }\n});\nexport type ProviderAugmentationContribution = z.infer<typeof ProviderAugmentationContributionSchema>;\n\nexport interface RuntimeOwnerCompanionExports {\n providerContributions?: ProviderCompanionContribution[];\n /**\n * Operation augmentations of providers this package does not own (#1672). Separate from\n * `providerContributions` on purpose — see {@link ProviderAugmentationContributionSchema}.\n */\n providerAugmentations?: ProviderAugmentationContribution[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider-discovery.shared.schemas.js","sourceRoot":"","sources":["../../../../src/companion/provider-discovery.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { AppFrontendType } from '../config/app-frontend-type.shared.schemas';\nimport { EngineCapability } from '../external-providers/engine-capabilities.shared.schemas';\nimport { FrontendProviderEntrySchema } from '../external-providers/frontend-bootstrap.shared.schemas';\nimport { ProviderIdentitySchema, ProviderOriginSchema, ProviderTier } from '../external-providers/provider-metadata.shared.schemas';\nimport { ProviderComplianceProfileSchema } from '../external-providers/provider-compliance-profile.shared.schemas';\n\n/**\n * Companion-safe provider implementation declaration.\n *\n * App/runtime-owner packages expose these entries through their `./companion`\n * default export so tooling can discover which provider refs are actually\n * reachable from which runtime owners before materializing generated artifacts.\n */\n\n/**\n * Discriminator branch for `frontendService` runtime targets.\n *\n * Targets a frontend service by its `AppFrontendType` (the closed kind set\n * — SAAS_APP, STATIC_WEBSITE, TECH_DOC) rather than by an app-defined\n * `serviceKey` string. Engine-shipped packages (`external-connectors-public`,\n * etc.) have no way to know app-defined service keys, so the discovery\n * contract is keyed on the type. Apps with multiple frontend services of\n * the same type currently broadcast the contribution to all of them; if\n * per-service-key targeting becomes a need, add a second variant.\n */\nexport const ProviderFrontendServiceRuntimeTargetSchema = z.strictObject({\n kind: z.literal('frontendService'),\n serviceType: z.enum(AppFrontendType),\n});\nexport type ProviderFrontendServiceRuntimeTarget = z.infer<typeof ProviderFrontendServiceRuntimeTargetSchema>;\n\nexport const ProviderCompanionRuntimeTargetSchema = z.discriminatedUnion('kind', [\n z.strictObject({\n kind: z.literal('backend'),\n }),\n z.strictObject({\n kind: z.literal('companion'),\n }),\n ProviderFrontendServiceRuntimeTargetSchema,\n z.strictObject({\n kind: z.literal('minion'),\n minionKey: z.string().min(1),\n }),\n z.strictObject({\n kind: z.literal('worker'),\n workerKey: z.string().min(1),\n }),\n]);\nexport type ProviderCompanionRuntimeTarget = z.infer<typeof ProviderCompanionRuntimeTargetSchema>;\n\n/**\n * Values-free projection of one backend provider secret requirement.\n *\n * The stable provider-owned `slug` is preserved separately from the concrete\n * environment-variable name so a rename on either axis changes the lockfile\n * integrity digest. Secret values never cross this companion-safe boundary.\n */\nexport const ProviderSecretContractShapeEntrySchema = z.strictObject({\n slug: z.string().min(1),\n envVarName: z.string().min(1),\n required: z.boolean(),\n description: z.string().min(1),\n});\nexport type ProviderSecretContractShapeEntry = z.infer<typeof ProviderSecretContractShapeEntrySchema>;\n\n/**\n * Deterministic, values-free secret-contract shape used by provider discovery\n * and lockfile integrity checks.\n *\n * Every contribution must declare this field. `[]` means the provider is\n * authoritatively known to require no secrets; omission is rejected rather\n * than being silently interpreted as an empty contract. Entries must be\n * sorted by `slug`, and both slugs and environment-variable names are unique,\n * so independently contributed facets cannot produce order-dependent digests.\n */\nexport const ProviderSecretContractShapeSchema = z.array(ProviderSecretContractShapeEntrySchema).superRefine((entries, context) => {\n const seenSlugs = new Set<string>();\n const seenEnvVarNames = new Set<string>();\n\n for (const [index, entry] of entries.entries()) {\n if (seenSlugs.has(entry.slug)) {\n context.addIssue({\n code: 'custom',\n path: [index, 'slug'],\n message: `Duplicate provider secret-contract slug \"${entry.slug}\".`,\n });\n }\n seenSlugs.add(entry.slug);\n\n if (seenEnvVarNames.has(entry.envVarName)) {\n context.addIssue({\n code: 'custom',\n path: [index, 'envVarName'],\n message: `Duplicate provider secret-contract envVarName \"${entry.envVarName}\".`,\n });\n }\n seenEnvVarNames.add(entry.envVarName);\n\n if (index > 0 && entries[index - 1].slug.localeCompare(entry.slug) >= 0) {\n context.addIssue({\n code: 'custom',\n path: [index, 'slug'],\n message: 'Provider secret-contract entries must be sorted by slug.',\n });\n }\n }\n});\nexport type ProviderSecretContractShape = z.infer<typeof ProviderSecretContractShapeSchema>;\n\n/**\n * A contribution is a wire record produced by one package version and read by\n * another (the CLI, the companion, a generated artifact), so it is a LOOSE\n * object: an additive field from a newer producer rides through an older\n * consumer instead of hard-failing the parse (#328 — it used to be strict,\n * which made every additive field a five-surface change and every old/new\n * mix a boot failure). Required fields stay required; only UNKNOWN keys are\n * tolerated, and they are kept rather than stripped so a re-emitting\n * consumer does not silently drop what it did not understand.\n */\nexport const ProviderCompanionContributionSchema = z.looseObject({\n ref: z.string().min(1),\n /**\n * The provider's identity (package, tier, vendor, origin) as declared on\n * its module metadata, so tooling can display tiers and group vendors\n * without loading the runtime module. Optional on the wire for one\n * reason only: contributions emitted before #328 carry none, and a\n * discovery walk must still read them.\n */\n identity: ProviderIdentitySchema.optional(),\n /**\n * Stable package import path that makes the provider implementation reachable\n * from the contributing package at runtime. This validates owner-package\n * reachability without coupling discovery to source-file paths.\n */\n runtimeImportPath: z.string().min(1),\n runtimeTargets: z.array(ProviderCompanionRuntimeTargetSchema).min(1),\n engineCapabilities: z.array(z.enum(EngineCapability)).default([]),\n providerCapabilities: z.array(z.string().min(1)).default([]),\n protocols: z.array(z.string().min(1)).default([]),\n secretsContractShape: ProviderSecretContractShapeSchema,\n /**\n * Per protocol, the environment variable its API key is read from — the DEFAULT `authConfig.secretRef`\n * a running host derives from the binding's runtime contract, so the value `getProviderApiKey` reads\n * before it refuses a call for a missing key (#2229). Keyed by the same protocol strings as `protocols`;\n * a protocol absent from the map authenticates with nothing a host must hold.\n *\n * Carried so tooling can predict that refusal from discovery without loading a provider module — the\n * creation preflight names the ONE key a step needs, where `secretsContractShape` lists every key the\n * provider may use (Stripe's webhook secret is required by its shape and read by no boot refusal).\n * Values-free, and optional on the wire because a contribution emitted before #2229 carries none; a\n * reader then falls back to the required entries of `secretsContractShape`.\n */\n protocolSecretRefs: z.record(z.string().min(1), z.string().min(1)).optional(),\n /**\n * Optional frontend-safe provider payload for `frontendService` runtime\n * targets. Tooling materializes this into generated runtime artifacts so the\n * backend bootstrap endpoint can serve app-owned public providers without\n * importing frontend packages at runtime.\n */\n frontendEntry: FrontendProviderEntrySchema.optional(),\n /**\n * Optional app module owner when the provider implementation belongs to one\n * concrete module instead of the whole package/runtime.\n */\n sourceModuleId: z.string().min(1).optional(),\n /**\n * The hosts the provider's OAuth2 TOKEN endpoint may resolve to (#1725) — derived by the producer with\n * `describeProviderOAuth2TokenEndpointHosts`, absent when the provider runs no OAuth2 connected account.\n *\n * Carried so `wildo config sync` can refuse a declared shared connection whose member names a different\n * authorization server than its base, without loading either module. Values-free: a login host is not a\n * secret. Optional on the wire because a contribution emitted before #1725 carries none; the registry then\n * makes the same comparison on the loaded modules at boot.\n */\n oauth2TokenEndpointHosts: z.strictObject({\n exact: z.array(z.string().min(1)),\n suffixes: z.array(z.string().min(1)),\n undeterminable: z.boolean(),\n }).optional(),\n /**\n * The secrets-contract SLUGS holding the provider's connected-account OAuth2 client registration — its client id\n * and client secret (#1725). Absent when the provider runs no OAuth2 connected account.\n *\n * Carried so `wildo secrets` can tell an operator which registration a declared shared connection actually needs:\n * the BASE's slots are then required, and a MEMBER's own slots are inert — reported, never required — because the\n * base's client connects and refreshes the one grant.\n */\n oauth2ClientSlotSlugs: z.array(z.string().min(1)).optional(),\n /**\n * What the provider's BACKEND module declares about who runs it and where it processes (#1708) — ESTABLISHED\n * facts, or UNDETERMINED. A frontend facet's profile rides `frontendEntry.complianceProfile` instead (#1743).\n *\n * Carried so the compliance readers (the evidence collector, the companion's drafting brief) learn every\n * declared provider's profile from discovery, WITHOUT loading a provider module — the same reason\n * `oauth2TokenEndpointHosts` is here. Values-free: a vendor's region list is not a secret. Optional on the wire\n * because a contribution emitted before #1708 carries none, which a reader reports as \"no profile\".\n */\n complianceProfile: ProviderComplianceProfileSchema.optional(),\n});\nexport type ProviderCompanionContribution = z.infer<typeof ProviderCompanionContributionSchema>;\n\n/**\n * A package's declaration that it ADDS OPERATIONS to a provider it does not own (#1672).\n *\n * ## Why this is its own record and not a `ProviderCompanionContribution`\n *\n * A contribution says \"this package OWNS provider X\": identity, secrets contract, protocols, the runtimes\n * it reaches. Discovery keeps one owner per ref and compares every contribution's secrets-contract shape\n * against the others. An augmentation owns none of that — the base keeps identity, tier, secrets contract,\n * OAuth2 block and connection — so recording it as a second contribution of the same ref would make the\n * shape comparison treat it as a second owner, and the import map would pick one of the two by sort order.\n * A separate record is what lets the base stay the base.\n *\n * ## What it carries, and why each field is STATIC\n *\n * `operationIds` and `requiredScopes` are stated here rather than read from the catalogue because a\n * catalogue is a LAZY loader that tooling must never call (`ProviderRestOperationCatalogueLoader`). So\n * `wildo config sync` can refuse an operation-id collision and record which scopes an augmentation may ask\n * for without evaluating any vendor code; the runtime then checks that what the loader delivers is exactly\n * what was declared here, and refuses the augmentation otherwise.\n *\n * INERT until an application opts it in under `providers.augmentations.<augments>` — installing a package\n * never adds an operation to a provider on its own.\n *\n * Loose for the reason `ProviderCompanionContributionSchema` is: a newer producer's additive field must ride\n * through an older consumer.\n */\nexport const ProviderAugmentationContributionSchema = z.looseObject({\n /** The ref of the provider this package adds operations to — owned by the engine or another package. */\n augments: z.string().min(1),\n /** The augmentation package's own npm name, as its manifest states it. */\n packageName: z.string().min(1),\n /** Always `CATALOGUE`: an augmentation binds no engine slot and adds only operations. */\n tier: z.literal(ProviderTier.CATALOGUE),\n origin: ProviderOriginSchema,\n /** The specifier a host imports to read the augmentation (`<package>/backend` by convention). */\n runtimeImportPath: z.string().min(1),\n /** Every operation id the augmentation contributes — sorted, unique, non-empty. */\n operationIds: z.array(z.string().min(1)).min(1),\n /** Every vendor scope any of those operations declares in `requiredScopes` — sorted, unique. */\n requiredScopes: z.array(z.string().min(1)).default([]),\n}).superRefine((contribution, ctx) => {\n for (const [field, values] of [['operationIds', contribution.operationIds], ['requiredScopes', contribution.requiredScopes]] as const) {\n for (let index = 1; index < values.length; index += 1) {\n if (values[index - 1].localeCompare(values[index]) >= 0) {\n ctx.addIssue({ code: 'custom', path: [field, index], message: `${field} must be sorted and unique (\"${values[index]}\" follows \"${values[index - 1]}\").` });\n }\n }\n }\n});\nexport type ProviderAugmentationContribution = z.infer<typeof ProviderAugmentationContributionSchema>;\n\nexport interface RuntimeOwnerCompanionExports {\n providerContributions?: ProviderCompanionContribution[];\n /**\n * Operation augmentations of providers this package does not own (#1672). Separate from\n * `providerContributions` on purpose — see {@link ProviderAugmentationContributionSchema}.\n */\n providerAugmentations?: ProviderAugmentationContribution[];\n}\n"]}
|
|
@@ -311,6 +311,35 @@ export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
|
|
|
311
311
|
* `rights-and-complaint-route` fact then answers nothing (#454).
|
|
312
312
|
*/
|
|
313
313
|
subjectRequestIdentityVerification: z.ZodOptional<z.ZodString>;
|
|
314
|
+
/**
|
|
315
|
+
* Where the operator's OWN infrastructure hosts and processes the application's data — "France", "eu-west-3",
|
|
316
|
+
* "Germany (primary), Netherlands (backups)". At least one when declared.
|
|
317
|
+
*
|
|
318
|
+
* ## Why it is here, and why nothing else answered it
|
|
319
|
+
*
|
|
320
|
+
* Every other region in the compliance model belongs to a THIRD party: `providerComplianceChoices.<ref>.processingRegion`
|
|
321
|
+
* and the processor register's `processingRegions` say where a vendor processes. Nothing said where the application
|
|
322
|
+
* itself runs (#2238), so a creation run could only carry it as a free-text interview answer ("hosting provider and
|
|
323
|
+
* exact EU/EEA region: not yet decided"), which no derivation can read and the launch list could not name. Where the
|
|
324
|
+
* operator hosts is the first thing an international-transfer statement needs, and it is the operator's decision about
|
|
325
|
+
* its own product, so it sits with the operator's identity.
|
|
326
|
+
*
|
|
327
|
+
* Free text per region, like the processor register's `processingRegions`: a cloud region id and a country are both
|
|
328
|
+
* answers a reader can act on, and the law names neither. A hosting company that processes on the operator's behalf is
|
|
329
|
+
* ALSO a processor and belongs in the processor register; this field says where the data is, not who holds it.
|
|
330
|
+
*
|
|
331
|
+
* ## Rejected: deriving it from `remoteProvider.region`
|
|
332
|
+
*
|
|
333
|
+
* Each remote environment's `wildo.infra.remote.config.ts` may carry `remoteProvider.region` (`WildoRemoteProviderSchema`).
|
|
334
|
+
* It is not this statement and is not read as one: it is absent at creation time (no environment exists yet), it is per
|
|
335
|
+
* ENVIRONMENT where a notice states one answer for the production data, it describes the cluster and its registry rather
|
|
336
|
+
* than everywhere the data lands (managed databases, object storage, backups), and it is deployment metadata nobody
|
|
337
|
+
* reviews as a statement to a data subject. Two declarations of "where we run" can disagree; this one is the one a notice
|
|
338
|
+
* quotes, and an operator who keeps both should keep them consistent.
|
|
339
|
+
*
|
|
340
|
+
* Absent means undecided, never "hosted nowhere".
|
|
341
|
+
*/
|
|
342
|
+
hostingRegions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
314
343
|
}, z.core.$strict>;
|
|
315
344
|
export type ApplicationOperatorComplianceIdentity = z.infer<typeof ApplicationOperatorComplianceIdentitySchema>;
|
|
316
345
|
/**
|
|
@@ -332,7 +361,9 @@ export declare enum OperatorComplianceIdentityPart {
|
|
|
332
361
|
PRIVACY_CONTACT = "privacyContact",
|
|
333
362
|
ROLE_ALLOCATIONS = "roleAllocations",
|
|
334
363
|
LAWFUL_BASES = "lawfulBases",
|
|
335
|
-
SUBJECT_REQUEST_IDENTITY_VERIFICATION = "subjectRequestIdentityVerification"
|
|
364
|
+
SUBJECT_REQUEST_IDENTITY_VERIFICATION = "subjectRequestIdentityVerification",
|
|
365
|
+
/** Where the operator's own infrastructure hosts the data (#2238). Owed by every operator: an application is hosted somewhere. */
|
|
366
|
+
HOSTING_REGIONS = "hostingRegions"
|
|
336
367
|
}
|
|
337
368
|
/**
|
|
338
369
|
* Which parts of the identity are still owed, in declaration order.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator-compliance-identity.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D;;;;;;;;;;GAUG;AACH,oBAAY,WAAW;IACrB,gGAAgG;IAChG,OAAO,YAAY;IACnB,iGAAiG;IACjG,QAAQ,aAAa;IACrB,+FAA+F;IAC/F,gBAAgB,qBAAqB;IACrC,4FAA4F;IAC5F,eAAe,oBAAoB;IACnC,wFAAwF;IACxF,WAAW,gBAAgB;IAC3B;;;;OAIG;IACH,oBAAoB,yBAAyB;CAC9C;AAED,6GAA6G;AAC7G,oBAAY,sBAAsB;IAChC,sDAAsD;IACtD,UAAU,eAAe;IACzB,mHAAmH;IACnH,SAAS,cAAc;IACvB,oHAAoH;IACpH,gBAAgB,qBAAqB;CACtC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B;IACvC,oGAAoG;;;IAGpG;;;;;OAKG;;kBAEH,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;IACpC,0GAA0G;;IAE1G;;;;;;;;OAQG;;;IAGH;;;OAGG;;kBAgBH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,+BAA+B;IAC1C;;;;;;;;;OASG;;IAEH;;;;;;OAMG;;kBA2BH,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,2CAA2C;IACtD,2HAA2H;;IAE3H,qGAAqG;;IAErG;;;;;OAKG;;IAEH;;;;;;OAMG;;;;;IAKH;;;;OAIG;;QA/NH,oGAAoG;;;QAGpG;;;;;WAKG;;;IAyNH;;;;;;OAMG;;QApNH,0GAA0G;;QAE1G;;;;;;;;WAQG;;;QAGH;;;WAGG;;;IAgNH;;;;;;OAMG;;QAjJH;;;;;;;;;WASG;;QAEH;;;;;;WAMG;;;IAkIH;;;;;;;;;OASG;;kBAsBH,CAAC;AACH,MAAM,MAAM,qCAAqC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2CAA2C,CAAC,CAAC;AAGhH;;;;;;;;;;;;GAYG;AACH,oBAAY,8BAA8B;IACxC,iBAAiB,oBAAoB;IACrC,kBAAkB,sBAAsB;IACxC,eAAe,mBAAmB;IAClC,gBAAgB,oBAAoB;IACpC,YAAY,gBAAgB;IAC5B,qCAAqC,uCAAuC;
|
|
1
|
+
{"version":3,"file":"operator-compliance-identity.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D;;;;;;;;;;GAUG;AACH,oBAAY,WAAW;IACrB,gGAAgG;IAChG,OAAO,YAAY;IACnB,iGAAiG;IACjG,QAAQ,aAAa;IACrB,+FAA+F;IAC/F,gBAAgB,qBAAqB;IACrC,4FAA4F;IAC5F,eAAe,oBAAoB;IACnC,wFAAwF;IACxF,WAAW,gBAAgB;IAC3B;;;;OAIG;IACH,oBAAoB,yBAAyB;CAC9C;AAED,6GAA6G;AAC7G,oBAAY,sBAAsB;IAChC,sDAAsD;IACtD,UAAU,eAAe;IACzB,mHAAmH;IACnH,SAAS,cAAc;IACvB,oHAAoH;IACpH,gBAAgB,qBAAqB;CACtC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B;IACvC,oGAAoG;;;IAGpG;;;;;OAKG;;kBAEH,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;IACpC,0GAA0G;;IAE1G;;;;;;;;OAQG;;;IAGH;;;OAGG;;kBAgBH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,+BAA+B;IAC1C;;;;;;;;;OASG;;IAEH;;;;;;OAMG;;kBA2BH,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,2CAA2C;IACtD,2HAA2H;;IAE3H,qGAAqG;;IAErG;;;;;OAKG;;IAEH;;;;;;OAMG;;;;;IAKH;;;;OAIG;;QA/NH,oGAAoG;;;QAGpG;;;;;WAKG;;;IAyNH;;;;;;OAMG;;QApNH,0GAA0G;;QAE1G;;;;;;;;WAQG;;;QAGH;;;WAGG;;;IAgNH;;;;;;OAMG;;QAjJH;;;;;;;;;WASG;;QAEH;;;;;;WAMG;;;IAkIH;;;;;;;;;OASG;;IAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;;kBAsBH,CAAC;AACH,MAAM,MAAM,qCAAqC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2CAA2C,CAAC,CAAC;AAGhH;;;;;;;;;;;;GAYG;AACH,oBAAY,8BAA8B;IACxC,iBAAiB,oBAAoB;IACrC,kBAAkB,sBAAsB;IACxC,eAAe,mBAAmB;IAClC,gBAAgB,oBAAoB;IACpC,YAAY,gBAAgB;IAC5B,qCAAqC,uCAAuC;IAC5E,kIAAkI;IAClI,eAAe,mBAAmB;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,0CAA0C,CACxD,QAAQ,EAAE,qCAAqC,GAAG,SAAS,GAC1D,SAAS,8BAA8B,EAAE,CAU3C;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qCAAqC,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,CAKvH;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,qCAAqC,GAAG,OAAO,GAAG,SAAS,CAGxH"}
|
|
@@ -311,6 +311,35 @@ export const ApplicationOperatorComplianceIdentitySchema = z.strictObject({
|
|
|
311
311
|
* `rights-and-complaint-route` fact then answers nothing (#454).
|
|
312
312
|
*/
|
|
313
313
|
subjectRequestIdentityVerification: z.string().trim().min(1).optional(),
|
|
314
|
+
/**
|
|
315
|
+
* Where the operator's OWN infrastructure hosts and processes the application's data — "France", "eu-west-3",
|
|
316
|
+
* "Germany (primary), Netherlands (backups)". At least one when declared.
|
|
317
|
+
*
|
|
318
|
+
* ## Why it is here, and why nothing else answered it
|
|
319
|
+
*
|
|
320
|
+
* Every other region in the compliance model belongs to a THIRD party: `providerComplianceChoices.<ref>.processingRegion`
|
|
321
|
+
* and the processor register's `processingRegions` say where a vendor processes. Nothing said where the application
|
|
322
|
+
* itself runs (#2238), so a creation run could only carry it as a free-text interview answer ("hosting provider and
|
|
323
|
+
* exact EU/EEA region: not yet decided"), which no derivation can read and the launch list could not name. Where the
|
|
324
|
+
* operator hosts is the first thing an international-transfer statement needs, and it is the operator's decision about
|
|
325
|
+
* its own product, so it sits with the operator's identity.
|
|
326
|
+
*
|
|
327
|
+
* Free text per region, like the processor register's `processingRegions`: a cloud region id and a country are both
|
|
328
|
+
* answers a reader can act on, and the law names neither. A hosting company that processes on the operator's behalf is
|
|
329
|
+
* ALSO a processor and belongs in the processor register; this field says where the data is, not who holds it.
|
|
330
|
+
*
|
|
331
|
+
* ## Rejected: deriving it from `remoteProvider.region`
|
|
332
|
+
*
|
|
333
|
+
* Each remote environment's `wildo.infra.remote.config.ts` may carry `remoteProvider.region` (`WildoRemoteProviderSchema`).
|
|
334
|
+
* It is not this statement and is not read as one: it is absent at creation time (no environment exists yet), it is per
|
|
335
|
+
* ENVIRONMENT where a notice states one answer for the production data, it describes the cluster and its registry rather
|
|
336
|
+
* than everywhere the data lands (managed databases, object storage, backups), and it is deployment metadata nobody
|
|
337
|
+
* reviews as a statement to a data subject. Two declarations of "where we run" can disagree; this one is the one a notice
|
|
338
|
+
* quotes, and an operator who keeps both should keep them consistent.
|
|
339
|
+
*
|
|
340
|
+
* Absent means undecided, never "hosted nowhere".
|
|
341
|
+
*/
|
|
342
|
+
hostingRegions: z.array(z.string().trim().min(1)).min(1).optional(),
|
|
314
343
|
}).superRefine((identity, context) => {
|
|
315
344
|
/*
|
|
316
345
|
* A declaration with nothing in it is a no-op that reads as an answer.
|
|
@@ -352,6 +381,8 @@ export var OperatorComplianceIdentityPart;
|
|
|
352
381
|
OperatorComplianceIdentityPart["ROLE_ALLOCATIONS"] = "roleAllocations";
|
|
353
382
|
OperatorComplianceIdentityPart["LAWFUL_BASES"] = "lawfulBases";
|
|
354
383
|
OperatorComplianceIdentityPart["SUBJECT_REQUEST_IDENTITY_VERIFICATION"] = "subjectRequestIdentityVerification";
|
|
384
|
+
/** Where the operator's own infrastructure hosts the data (#2238). Owed by every operator: an application is hosted somewhere. */
|
|
385
|
+
OperatorComplianceIdentityPart["HOSTING_REGIONS"] = "hostingRegions";
|
|
355
386
|
})(OperatorComplianceIdentityPart || (OperatorComplianceIdentityPart = {}));
|
|
356
387
|
/**
|
|
357
388
|
* Which parts of the identity are still owed, in declaration order.
|
|
@@ -369,6 +400,7 @@ export function outstandingOperatorComplianceIdentityParts(identity) {
|
|
|
369
400
|
identity?.roleAllocations === undefined ? OperatorComplianceIdentityPart.ROLE_ALLOCATIONS : undefined,
|
|
370
401
|
identity?.lawfulBases === undefined ? OperatorComplianceIdentityPart.LAWFUL_BASES : undefined,
|
|
371
402
|
identity?.subjectRequestIdentityVerification === undefined ? OperatorComplianceIdentityPart.SUBJECT_REQUEST_IDENTITY_VERIFICATION : undefined,
|
|
403
|
+
identity?.hostingRegions === undefined ? OperatorComplianceIdentityPart.HOSTING_REGIONS : undefined,
|
|
372
404
|
].filter((part) => part !== undefined);
|
|
373
405
|
}
|
|
374
406
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator-compliance-identity.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { ConsentPurpose } from './consent-purposes.shared';\nimport { LawfulBasisPurposeRefSchema } from './purpose-ref.shared.schemas';\n\n/**\n * GDPR Art. 6(1) — the six lawful bases, closed by the article.\n *\n * Enumerated individually rather than carried as prose because each carries a different obligation:\n * consent must be withdrawable, legitimate interests needs a balancing assessment, a legal obligation\n * must name the law. \"We have a lawful basis\" is not a disclosure; a notice must say WHICH.\n *\n * spec-cite: GDPR Art. 6(1)(a)–(f).\n *\n * assurance-control: WILDO.PRIVACY.LAWFUL_BASIS — the closed set a processing purpose's ground is declared from.\n */\nexport enum LawfulBasis {\n /** Art. 6(1)(a) — the subject consented to the processing for one or more specific purposes. */\n CONSENT = 'CONSENT',\n /** Art. 6(1)(b) — necessary to perform a contract with the subject, or pre-contractual steps. */\n CONTRACT = 'CONTRACT',\n /** Art. 6(1)(c) — necessary to comply with a legal obligation the controller is subject to. */\n LEGAL_OBLIGATION = 'LEGAL_OBLIGATION',\n /** Art. 6(1)(d) — necessary to protect vital interests of the subject or another person. */\n VITAL_INTERESTS = 'VITAL_INTERESTS',\n /** Art. 6(1)(e) — necessary for a task in the public interest or official authority. */\n PUBLIC_TASK = 'PUBLIC_TASK',\n /**\n * Art. 6(1)(f) — necessary for legitimate interests, except where overridden by the subject's\n * rights. The only basis requiring a balancing assessment, which is why declaring it without one\n * is refused below.\n */\n LEGITIMATE_INTERESTS = 'LEGITIMATE_INTERESTS',\n}\n\n/** Whether the operator decides the purposes of a processing, carries them out for someone else, or both. */\nexport enum OperatorProcessingRole {\n /** The operator determines the purposes and means. */\n CONTROLLER = 'CONTROLLER',\n /** The operator processes on a customer's documented instructions — the usual B2B posture for customer records. */\n PROCESSOR = 'PROCESSOR',\n /** The operator and another party jointly determine purposes and means, and must have an arrangement saying how. */\n JOINT_CONTROLLER = 'JOINT_CONTROLLER',\n}\n\n/**\n * ONE role, over ONE population of data.\n *\n * A single role field would be the wrong shape, and the produced notice said so before this existed:\n * *\"This notice therefore does not assign a single legal role to all data processed in a workspace.\"*\n * A B2B SaaS is typically a PROCESSOR for the records its customers put in, and a CONTROLLER for the\n * account and billing data of the people who administer them — simultaneously, in one product.\n *\n * `dataScope` is prose the operator writes, deliberately: it names a POPULATION of data (\"customer records in a\n * workspace\"), not a processing purpose, so a lawful basis's `purposeRef` (#1709) is not what it would key to. No\n * structured population vocabulary exists yet, and a ref to nothing would be worse than prose.\n */\nexport const OperatorRoleAllocationSchema = z.strictObject({\n /** Which data this role covers, in the operator's own words — \"customer records in a workspace\". */\n dataScope: z.string().trim().min(1),\n role: z.enum(OperatorProcessingRole),\n /**\n * The resource types this role covers — the TYPED half of `dataScope` (#1710), so a running erasure or export can\n * say who owes the Art. 19 communication for the subject in front of it. Optional: an allocation in prose only is\n * still a valid statement for a notice, and a subject whose resource type no allocation names is reported as\n * ROLE UNDECLARED, never guessed. `dataScope` stays the words a notice uses.\n */\n resourceTypes: z.array(z.string().trim().min(1)).min(1).optional(),\n});\nexport type OperatorRoleAllocation = z.infer<typeof OperatorRoleAllocationSchema>;\n\n/**\n * One processing purpose and the basis the operator relies on for it.\n *\n * assurance-control: WILDO.PRIVACY.LAWFUL_BASIS — refuses a legitimate-interests basis that records no balancing assessment.\n */\nexport const OperatorLawfulBasisSchema = z.strictObject({\n /** The purpose, in the operator's own words. Matches how the notice states purposes: prose, not a ref. */\n purpose: z.string().trim().min(1),\n /**\n * A STABLE id for this purpose, which a provider's or a system of record's compliance choice names\n * (`providerComplianceChoices.<ref>.purposeRefs`) to say which purpose sending it data serves (#1709).\n *\n * Separate from `purpose` because the prose is the notice's wording and changes when the operator rewords it, while\n * a link to it must survive the rewording. OPTIONAL, like every other part of this identity (#225): a basis with no\n * ref is still a basis, and a recipient cannot link to it — which the recipients report names as a purpose not\n * linked, never as a recipient with no purpose.\n */\n purposeRef: LawfulBasisPurposeRefSchema.optional(),\n basis: z.enum(LawfulBasis),\n /**\n * The assessment behind the basis. REQUIRED for `LEGITIMATE_INTERESTS`, which is the only basis\n * that needs a balancing test — see the superRefine below.\n */\n assessmentNote: z.string().trim().min(1).optional(),\n}).superRefine((entry, context) => {\n /*\n * A legitimate-interests claim with no assessment is the shape that satisfies a field and discloses\n * nothing — the same defect as a revision that records no superseded rationale. Art. 6(1)(f) is the\n * only basis whose lawfulness depends on a balancing test, so a declaration without one asserts a\n * conclusion nobody reached.\n */\n if (entry.basis === LawfulBasis.LEGITIMATE_INTERESTS && entry.assessmentNote === undefined) {\n context.addIssue({\n code: 'custom',\n path: ['assessmentNote'],\n message: `purpose \"${entry.purpose}\" relies on LEGITIMATE_INTERESTS and records no assessment — that basis is lawful only where the balancing test was actually done, so state it or choose the basis that applies`,\n });\n }\n});\nexport type OperatorLawfulBasis = z.infer<typeof OperatorLawfulBasisSchema>;\n\n/**\n * What a person must agree to in order to USE the service at all (issue #139).\n *\n * ## The near-miss this exists because of\n *\n * `analytics.requiresConsent` already exists and governs ANALYTICS alone. The compliance partial\n * described it as \"the minimum a person must give to use the service\" until 2026-08-30 — a much\n * larger claim about a much narrower field. A drafter following that wording would have published\n * that a person must consent to analytics in order to use the product: false, and false about the one\n * subject a reader is most likely to act on.\n *\n * Correcting it left a real hole, which a produced notice then reported in its own words:\n * *\"There is no declared surface for recording a service-wide consent requirement distinct from\n * analytics consent.\"* This is that surface.\n *\n * ## Why the required set may be empty, and usually should be\n *\n * Consent is ONE Art. 6(1) basis among six, and for most B2B SaaS the basis for core processing is\n * CONTRACT — a person uses the product because their employer signed for it, not because they\n * consented. A non-empty required set says the opposite, so declaring one is a claim about the\n * lawful basis and not merely a UI gate.\n *\n * An EMPTY set is therefore the honest answer for most applications: *nothing is gated on consent;\n * the lawful basis lives in {@link ApplicationOperatorComplianceIdentitySchema.lawfulBases}.*\n * A field that pushed applications toward declaring consent would be worse than the gap it closes.\n *\n * ## This is the ENFORCED half of the identity (#453)\n *\n * Everything else in {@link ApplicationOperatorComplianceIdentitySchema} is DECLARATION — it is\n * rendered into documents and checked for coherence, and nothing at runtime acts on it. This field is\n * the exception. `wildo config service-consent` projects it into the running application's\n * `compliance.serviceConsentRequirement`. Until the person has granted every purpose in\n * `requiredPurposes`, the backend refuses every operation they initiate — resource operations over REST,\n * MCP tools, queued operations, WebSocket rows and subscriptions, agent tool calls and retrieval, the\n * manual controllers (charts, analytics, features, flows-actors, provider operations and connections),\n * notification badge reads, reading or following an A2A task (its status, reply, artifacts and push\n * configuration), and every A2A or WebSocket conversational turn, whether started or resumed after an\n * approval — except their own context and preferences, where they grant it, and cancelling an A2A task they\n * already started. Signing in, refreshing a session and signing out are not resource operations and stay\n * open, so the person can always reach that decision. Updates the server pushes on its own (a badge count\n * changing) are not a person initiating anything and are not held back. The authenticated application shell shows the consent gate in\n * place of every signed-in route. A reader of\n * this schema must be able to tell the two apart without tracing the runtime, which is why it is said\n * here.\n *\n * ⚠️ Declaring purposes here does NOT make consent the lawful basis for anything. It states a gate.\n * The basis is declared per purpose, separately, and the two disagreeing is the shape a reviewer\n * needs to see rather than one the schema should quietly reconcile.\n */\nexport const ServiceConsentRequirementSchema = z.strictObject({\n /**\n * The purposes a person must grant before they may use the service — a SET, never a level (#453).\n *\n * EMPTY means nothing is gated on consent — the common and usually correct answer. It is a\n * DECLARATION, not an absence: an application that declares `[]` has answered the question, which is\n * what separates it from one whose `serviceConsentRequirement` is absent and was never asked.\n *\n * Replaces `requiredLevel`, which was typed on the ordered `DataProcessingConsent` and so claimed that\n * requiring MARKETING also required ANALYTICS. A required set names exactly what it gates on.\n */\n requiredPurposes: z.array(z.enum(ConsentPurpose)),\n /**\n * What this gate actually controls, in the operator's own words.\n *\n * REQUIRED whenever any purpose is required, because a gate nobody can describe is a gate nobody\n * can honour — enforced below. A notice stating \"consent is required\" without saying what it\n * governs tells a person they must agree to something unnamed.\n */\n gatedProcessing: z.string().trim().min(1).optional(),\n}).superRefine((requirement, context) => {\n /*\n * A required purpose with nothing described is the shape that satisfies the field and discloses\n * nothing — the same defect as a revision recording no superseded rationale, and as a\n * legitimate-interests basis with no assessment. Each time, the value is the easy half and the\n * explanation is the half that makes it mean anything.\n */\n if (requirement.requiredPurposes.length > 0 && requirement.gatedProcessing === undefined) {\n context.addIssue({\n code: 'custom',\n path: ['gatedProcessing'],\n message: `requiring consent to ${requirement.requiredPurposes.join(', ')} gates something — say what, or declare no required purposes. A notice stating that consent is required without naming what it governs tells a person they must agree to something unnamed`,\n });\n }\n /*\n * A purpose listed twice is not a stronger requirement, and a set that reads differently from how it\n * compares is how two surfaces come to describe one gate differently.\n */\n if (new Set(requirement.requiredPurposes).size !== requirement.requiredPurposes.length) {\n context.addIssue({\n code: 'custom',\n path: ['requiredPurposes'],\n message: 'a required purpose is listed more than once — a set names each purpose once',\n });\n }\n});\nexport type ServiceConsentRequirement = z.infer<typeof ServiceConsentRequirementSchema>;\n\n/**\n * WHO is publishing this notice, under what legal role, on what basis — the operator's own compliance\n * identity (issue #131).\n *\n * ## Why this exists, and why in configuration\n *\n * A produced compliance document asked three questions the application had nowhere to answer:\n * who operates the service, in what role, and on what lawful basis. Measured 2026-08-30, the only\n * `legalEntityName` anywhere in the compliance model was the PROCESSOR's, from the register in issue\n * #88 — nothing described the operator.\n *\n * That is not merely an empty field. A landing REPLACES the produced document on every run, so an\n * operator who answered by editing the document lost the answer on the next regeneration:\n * `.claude/rules/one-source-of-truth.md`'s test failing in the direction that matters — delete and\n * re-run, and you lose TRUTH rather than TIME.\n *\n * Configuration rather than a governed document, and identity and basis together rather than split.\n * They are two halves of ONE clause a reader meets in the first paragraph, and separating them would\n * put those halves in two places with two lifecycles for no benefit a reader can perceive. The\n * ASSESSMENT behind a basis is reviewed work and lives in `assessmentNote`; the conclusion belongs\n * with the identity that publishes it.\n *\n * ## What is deliberately NOT here\n *\n * A DPO appointment record, a processing-activities register (Art. 30), a retention schedule\n * (`dataRetention`), a processor register (#88). This is the operator's identity and legal posture —\n * the answers a notice needs in its opening sections — not a compliance programme.\n *\n * ## Every part is independently declarable, and that is the point (issue #228)\n *\n * This schema was optional AS A WHOLE and all-or-nothing INSIDE until 2026-09-02: the entity name,\n * address, contact, role allocations and lawful bases were each required, so an operator who knew\n * the registered name but had not settled lawful bases with counsel could record NOTHING. They\n * answered by editing the produced document, and a landing REPLACES it — losing the answer on the\n * next regeneration, which is the exact failure this schema was created to end.\n *\n * Paul's decision (#225) is that filling the entity must never block, and that generation is\n * resilient to what is missing while saying plainly that the result is incomplete. So every part is\n * optional and {@link outstandingOperatorComplianceIdentityParts} names what is still owed.\n *\n * What did NOT weaken: every refinement guarding a STATED claim. A legitimate-interests basis still\n * requires its assessment, and a non-empty required purpose set still requires what it gates. Those\n * refuse an INCOHERENT answer, never an ABSENT one, and the distinction is the whole design — a\n * partial declaration is honest, a self-contradicting one is not.\n */\nexport const ApplicationOperatorComplianceIdentitySchema = z.strictObject({\n /** The legal entity operating the service, as registered. Absent means unanswered — see the partial-filling note above. */\n legalEntityName: z.string().trim().min(1).optional(),\n /** Its registered address, as a reader would need it to serve a request. Absent means unanswered. */\n registeredAddress: z.string().trim().min(1).optional(),\n /**\n * How a data subject reaches the operator about privacy — an address, an email, or a URL.\n *\n * Open string because all three are legitimate and the law names none: what matters is that a route\n * exists and is stated, not what shape it takes.\n */\n privacyContact: z.string().trim().min(1).optional(),\n /**\n * An Art. 27 representative in the origin jurisdiction, where the operator is established outside it.\n *\n * Optional, because most operators are established where their subjects are and appointing one\n * would be a fiction. Absent means \"not required\", never \"not considered\" — a notice states it only\n * when declared.\n */\n representative: z.strictObject({\n name: z.string().trim().min(1),\n address: z.string().trim().min(1),\n }).optional(),\n /**\n * The operator's role, per population of data. At least one.\n *\n * See {@link OperatorRoleAllocationSchema} for why this is a list rather than a field.\n */\n roleAllocations: z.array(OperatorRoleAllocationSchema).min(1).optional(),\n /**\n * The lawful basis relied on, per processing purpose. At least one.\n *\n * `min(1)` at the schema for the same reason the placement plan requires one decision: an identity\n * declaring no basis at all is a refusal wearing a declaration's clothes, and it would land as a\n * complete answer.\n */\n lawfulBases: z.array(OperatorLawfulBasisSchema).min(1).superRefine((bases, context) => {\n // A purpose ref names ONE purpose, or a recipient linking to it would be linked to two (#1709).\n const seen = new Set<string>();\n bases.forEach((entry, index) => {\n if (entry.purposeRef === undefined) return;\n if (seen.has(entry.purposeRef)) {\n context.addIssue({ code: 'custom', path: [index, 'purposeRef'], message: `purpose ref \"${entry.purposeRef}\" names more than one purpose` });\n }\n seen.add(entry.purposeRef);\n });\n }).optional(),\n /**\n * What a person must consent to in order to use the service at all (issue #139).\n *\n * OPTIONAL, and absent means UNANSWERED rather than an empty set. The distinction is the one this\n * whole lane turns on: a declared `requiredPurposes: []` is the operator saying nothing is gated on consent, while\n * absence is nobody having been asked, and a notice must not render the second as the first.\n */\n serviceConsentRequirement: ServiceConsentRequirementSchema.optional(),\n /**\n * How the operator confirms that a person exercising a data-subject right is that person, before acting.\n *\n * The one part of the rights clause the law leaves to the operator: WHICH rights apply follows from the\n * jurisdictions and the lawful bases, and the complaint route from the regime, but the verification step\n * is a procedure the operator runs (GDPR Art. 12(6) lets a controller ask for what it reasonably needs).\n * Prose, because procedures differ — \"requests from a signed-in account are verified by that session;\n * requests by email are answered at the account's address\". Absent means undecided, and the\n * `rights-and-complaint-route` fact then answers nothing (#454).\n */\n subjectRequestIdentityVerification: z.string().trim().min(1).optional(),\n}).superRefine((identity, context) => {\n /*\n * A declaration with nothing in it is a no-op that reads as an answer.\n *\n * Every part is independently optional so an operator can record the half they know (#228), and the\n * cost of that is a new shape the old schema could not express: `operatorComplianceIdentity: {}`.\n * Absent and empty must not mean the same thing — absence is \"nobody was asked\", which every\n * consumer already renders as outstanding, while an empty object would satisfy the field and\n * disclose nothing. Same defect the file refuses twice already, for a legitimate-interests basis\n * with no assessment and a consent gate that names nothing.\n */\n if (Object.values(identity).every((part) => part === undefined)) {\n context.addIssue({\n code: 'custom',\n path: [],\n message:\n 'an operator compliance identity that declares nothing is not a partial answer, it is an empty one — '\n + 'record the part you know, or omit `operatorComplianceIdentity` entirely so it reads as unanswered',\n });\n }\n});\nexport type ApplicationOperatorComplianceIdentity = z.infer<typeof ApplicationOperatorComplianceIdentitySchema>;\n\n\n/**\n * The parts of an operator's compliance identity, so \"what is still owed\" has one named home.\n *\n * A closed vocabulary rather than free strings: every consumer that reports an incomplete identity —\n * a drafter's brief, a document's marked gap, an operator-facing check — must name the same parts by\n * the same names, or two surfaces tell an operator to fill two different things.\n *\n * `representative` and `serviceConsentRequirement` are deliberately absent from this list. Both are\n * legitimately unset on a COMPLETE identity: an operator established where its subjects are needs no\n * Art. 27 representative, and a declared absence of a consent gate is expressed by\n * `serviceConsentRequirement.requiredPurposes = []`, which is a value rather than an omission. Listing\n * them would report every correct identity as incomplete.\n */\nexport enum OperatorComplianceIdentityPart {\n LEGAL_ENTITY_NAME = 'legalEntityName',\n REGISTERED_ADDRESS = 'registeredAddress',\n PRIVACY_CONTACT = 'privacyContact',\n ROLE_ALLOCATIONS = 'roleAllocations',\n LAWFUL_BASES = 'lawfulBases',\n SUBJECT_REQUEST_IDENTITY_VERIFICATION = 'subjectRequestIdentityVerification',\n}\n\n/**\n * Which parts of the identity are still owed, in declaration order.\n *\n * Empty means complete. An UNDECLARED identity is not this function's case — pass it and you get every\n * part back, which is correct but says less than the caller needs: \"nobody has been asked\" and \"four of\n * five parts are outstanding\" are different things to tell an operator, and the caller distinguishes\n * them by checking for the identity itself first.\n */\nexport function outstandingOperatorComplianceIdentityParts(\n identity: ApplicationOperatorComplianceIdentity | undefined,\n): readonly OperatorComplianceIdentityPart[] {\n return [\n identity?.legalEntityName === undefined ? OperatorComplianceIdentityPart.LEGAL_ENTITY_NAME : undefined,\n identity?.registeredAddress === undefined ? OperatorComplianceIdentityPart.REGISTERED_ADDRESS : undefined,\n identity?.privacyContact === undefined ? OperatorComplianceIdentityPart.PRIVACY_CONTACT : undefined,\n identity?.roleAllocations === undefined ? OperatorComplianceIdentityPart.ROLE_ALLOCATIONS : undefined,\n identity?.lawfulBases === undefined ? OperatorComplianceIdentityPart.LAWFUL_BASES : undefined,\n identity?.subjectRequestIdentityVerification === undefined ? OperatorComplianceIdentityPart.SUBJECT_REQUEST_IDENTITY_VERIFICATION : undefined,\n ].filter((part): part is OperatorComplianceIdentityPart => part !== undefined);\n}\n\n/**\n * The lawful bases an operator declares, deduplicated — what a notice enumerates when it says which\n * bases the service relies on.\n *\n * `undefined` when none are declared, and NOT an empty array. A notice rendering an empty list would\n * say the service relies on no lawful basis, which is a claim nobody made and one no processing could\n * survive; the caller has to meet the absent case rather than fall into it.\n */\nexport function declaredLawfulBases(identity: ApplicationOperatorComplianceIdentity): readonly LawfulBasis[] | undefined {\n if (identity.lawfulBases === undefined) return undefined;\n const order = Object.values(LawfulBasis);\n return [...new Set(identity.lawfulBases.map((entry) => entry.basis))]\n .sort((left, right) => order.indexOf(left) - order.indexOf(right));\n}\n\n/**\n * True when the operator declares more than one role, so a notice must not assign a single one.\n *\n * `undefined` when no roles are declared — the question cannot be answered, and `false` would be read\n * as \"one role\", licensing exactly the collapse {@link OperatorRoleAllocationSchema} exists to prevent.\n */\nexport function operatorHoldsSeveralProcessingRoles(identity: ApplicationOperatorComplianceIdentity): boolean | undefined {\n if (identity.roleAllocations === undefined) return undefined;\n return new Set(identity.roleAllocations.map((allocation) => allocation.role)).size > 1;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"operator-compliance-identity.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\nimport { ConsentPurpose } from './consent-purposes.shared';\nimport { LawfulBasisPurposeRefSchema } from './purpose-ref.shared.schemas';\n\n/**\n * GDPR Art. 6(1) — the six lawful bases, closed by the article.\n *\n * Enumerated individually rather than carried as prose because each carries a different obligation:\n * consent must be withdrawable, legitimate interests needs a balancing assessment, a legal obligation\n * must name the law. \"We have a lawful basis\" is not a disclosure; a notice must say WHICH.\n *\n * spec-cite: GDPR Art. 6(1)(a)–(f).\n *\n * assurance-control: WILDO.PRIVACY.LAWFUL_BASIS — the closed set a processing purpose's ground is declared from.\n */\nexport enum LawfulBasis {\n /** Art. 6(1)(a) — the subject consented to the processing for one or more specific purposes. */\n CONSENT = 'CONSENT',\n /** Art. 6(1)(b) — necessary to perform a contract with the subject, or pre-contractual steps. */\n CONTRACT = 'CONTRACT',\n /** Art. 6(1)(c) — necessary to comply with a legal obligation the controller is subject to. */\n LEGAL_OBLIGATION = 'LEGAL_OBLIGATION',\n /** Art. 6(1)(d) — necessary to protect vital interests of the subject or another person. */\n VITAL_INTERESTS = 'VITAL_INTERESTS',\n /** Art. 6(1)(e) — necessary for a task in the public interest or official authority. */\n PUBLIC_TASK = 'PUBLIC_TASK',\n /**\n * Art. 6(1)(f) — necessary for legitimate interests, except where overridden by the subject's\n * rights. The only basis requiring a balancing assessment, which is why declaring it without one\n * is refused below.\n */\n LEGITIMATE_INTERESTS = 'LEGITIMATE_INTERESTS',\n}\n\n/** Whether the operator decides the purposes of a processing, carries them out for someone else, or both. */\nexport enum OperatorProcessingRole {\n /** The operator determines the purposes and means. */\n CONTROLLER = 'CONTROLLER',\n /** The operator processes on a customer's documented instructions — the usual B2B posture for customer records. */\n PROCESSOR = 'PROCESSOR',\n /** The operator and another party jointly determine purposes and means, and must have an arrangement saying how. */\n JOINT_CONTROLLER = 'JOINT_CONTROLLER',\n}\n\n/**\n * ONE role, over ONE population of data.\n *\n * A single role field would be the wrong shape, and the produced notice said so before this existed:\n * *\"This notice therefore does not assign a single legal role to all data processed in a workspace.\"*\n * A B2B SaaS is typically a PROCESSOR for the records its customers put in, and a CONTROLLER for the\n * account and billing data of the people who administer them — simultaneously, in one product.\n *\n * `dataScope` is prose the operator writes, deliberately: it names a POPULATION of data (\"customer records in a\n * workspace\"), not a processing purpose, so a lawful basis's `purposeRef` (#1709) is not what it would key to. No\n * structured population vocabulary exists yet, and a ref to nothing would be worse than prose.\n */\nexport const OperatorRoleAllocationSchema = z.strictObject({\n /** Which data this role covers, in the operator's own words — \"customer records in a workspace\". */\n dataScope: z.string().trim().min(1),\n role: z.enum(OperatorProcessingRole),\n /**\n * The resource types this role covers — the TYPED half of `dataScope` (#1710), so a running erasure or export can\n * say who owes the Art. 19 communication for the subject in front of it. Optional: an allocation in prose only is\n * still a valid statement for a notice, and a subject whose resource type no allocation names is reported as\n * ROLE UNDECLARED, never guessed. `dataScope` stays the words a notice uses.\n */\n resourceTypes: z.array(z.string().trim().min(1)).min(1).optional(),\n});\nexport type OperatorRoleAllocation = z.infer<typeof OperatorRoleAllocationSchema>;\n\n/**\n * One processing purpose and the basis the operator relies on for it.\n *\n * assurance-control: WILDO.PRIVACY.LAWFUL_BASIS — refuses a legitimate-interests basis that records no balancing assessment.\n */\nexport const OperatorLawfulBasisSchema = z.strictObject({\n /** The purpose, in the operator's own words. Matches how the notice states purposes: prose, not a ref. */\n purpose: z.string().trim().min(1),\n /**\n * A STABLE id for this purpose, which a provider's or a system of record's compliance choice names\n * (`providerComplianceChoices.<ref>.purposeRefs`) to say which purpose sending it data serves (#1709).\n *\n * Separate from `purpose` because the prose is the notice's wording and changes when the operator rewords it, while\n * a link to it must survive the rewording. OPTIONAL, like every other part of this identity (#225): a basis with no\n * ref is still a basis, and a recipient cannot link to it — which the recipients report names as a purpose not\n * linked, never as a recipient with no purpose.\n */\n purposeRef: LawfulBasisPurposeRefSchema.optional(),\n basis: z.enum(LawfulBasis),\n /**\n * The assessment behind the basis. REQUIRED for `LEGITIMATE_INTERESTS`, which is the only basis\n * that needs a balancing test — see the superRefine below.\n */\n assessmentNote: z.string().trim().min(1).optional(),\n}).superRefine((entry, context) => {\n /*\n * A legitimate-interests claim with no assessment is the shape that satisfies a field and discloses\n * nothing — the same defect as a revision that records no superseded rationale. Art. 6(1)(f) is the\n * only basis whose lawfulness depends on a balancing test, so a declaration without one asserts a\n * conclusion nobody reached.\n */\n if (entry.basis === LawfulBasis.LEGITIMATE_INTERESTS && entry.assessmentNote === undefined) {\n context.addIssue({\n code: 'custom',\n path: ['assessmentNote'],\n message: `purpose \"${entry.purpose}\" relies on LEGITIMATE_INTERESTS and records no assessment — that basis is lawful only where the balancing test was actually done, so state it or choose the basis that applies`,\n });\n }\n});\nexport type OperatorLawfulBasis = z.infer<typeof OperatorLawfulBasisSchema>;\n\n/**\n * What a person must agree to in order to USE the service at all (issue #139).\n *\n * ## The near-miss this exists because of\n *\n * `analytics.requiresConsent` already exists and governs ANALYTICS alone. The compliance partial\n * described it as \"the minimum a person must give to use the service\" until 2026-08-30 — a much\n * larger claim about a much narrower field. A drafter following that wording would have published\n * that a person must consent to analytics in order to use the product: false, and false about the one\n * subject a reader is most likely to act on.\n *\n * Correcting it left a real hole, which a produced notice then reported in its own words:\n * *\"There is no declared surface for recording a service-wide consent requirement distinct from\n * analytics consent.\"* This is that surface.\n *\n * ## Why the required set may be empty, and usually should be\n *\n * Consent is ONE Art. 6(1) basis among six, and for most B2B SaaS the basis for core processing is\n * CONTRACT — a person uses the product because their employer signed for it, not because they\n * consented. A non-empty required set says the opposite, so declaring one is a claim about the\n * lawful basis and not merely a UI gate.\n *\n * An EMPTY set is therefore the honest answer for most applications: *nothing is gated on consent;\n * the lawful basis lives in {@link ApplicationOperatorComplianceIdentitySchema.lawfulBases}.*\n * A field that pushed applications toward declaring consent would be worse than the gap it closes.\n *\n * ## This is the ENFORCED half of the identity (#453)\n *\n * Everything else in {@link ApplicationOperatorComplianceIdentitySchema} is DECLARATION — it is\n * rendered into documents and checked for coherence, and nothing at runtime acts on it. This field is\n * the exception. `wildo config service-consent` projects it into the running application's\n * `compliance.serviceConsentRequirement`. Until the person has granted every purpose in\n * `requiredPurposes`, the backend refuses every operation they initiate — resource operations over REST,\n * MCP tools, queued operations, WebSocket rows and subscriptions, agent tool calls and retrieval, the\n * manual controllers (charts, analytics, features, flows-actors, provider operations and connections),\n * notification badge reads, reading or following an A2A task (its status, reply, artifacts and push\n * configuration), and every A2A or WebSocket conversational turn, whether started or resumed after an\n * approval — except their own context and preferences, where they grant it, and cancelling an A2A task they\n * already started. Signing in, refreshing a session and signing out are not resource operations and stay\n * open, so the person can always reach that decision. Updates the server pushes on its own (a badge count\n * changing) are not a person initiating anything and are not held back. The authenticated application shell shows the consent gate in\n * place of every signed-in route. A reader of\n * this schema must be able to tell the two apart without tracing the runtime, which is why it is said\n * here.\n *\n * ⚠️ Declaring purposes here does NOT make consent the lawful basis for anything. It states a gate.\n * The basis is declared per purpose, separately, and the two disagreeing is the shape a reviewer\n * needs to see rather than one the schema should quietly reconcile.\n */\nexport const ServiceConsentRequirementSchema = z.strictObject({\n /**\n * The purposes a person must grant before they may use the service — a SET, never a level (#453).\n *\n * EMPTY means nothing is gated on consent — the common and usually correct answer. It is a\n * DECLARATION, not an absence: an application that declares `[]` has answered the question, which is\n * what separates it from one whose `serviceConsentRequirement` is absent and was never asked.\n *\n * Replaces `requiredLevel`, which was typed on the ordered `DataProcessingConsent` and so claimed that\n * requiring MARKETING also required ANALYTICS. A required set names exactly what it gates on.\n */\n requiredPurposes: z.array(z.enum(ConsentPurpose)),\n /**\n * What this gate actually controls, in the operator's own words.\n *\n * REQUIRED whenever any purpose is required, because a gate nobody can describe is a gate nobody\n * can honour — enforced below. A notice stating \"consent is required\" without saying what it\n * governs tells a person they must agree to something unnamed.\n */\n gatedProcessing: z.string().trim().min(1).optional(),\n}).superRefine((requirement, context) => {\n /*\n * A required purpose with nothing described is the shape that satisfies the field and discloses\n * nothing — the same defect as a revision recording no superseded rationale, and as a\n * legitimate-interests basis with no assessment. Each time, the value is the easy half and the\n * explanation is the half that makes it mean anything.\n */\n if (requirement.requiredPurposes.length > 0 && requirement.gatedProcessing === undefined) {\n context.addIssue({\n code: 'custom',\n path: ['gatedProcessing'],\n message: `requiring consent to ${requirement.requiredPurposes.join(', ')} gates something — say what, or declare no required purposes. A notice stating that consent is required without naming what it governs tells a person they must agree to something unnamed`,\n });\n }\n /*\n * A purpose listed twice is not a stronger requirement, and a set that reads differently from how it\n * compares is how two surfaces come to describe one gate differently.\n */\n if (new Set(requirement.requiredPurposes).size !== requirement.requiredPurposes.length) {\n context.addIssue({\n code: 'custom',\n path: ['requiredPurposes'],\n message: 'a required purpose is listed more than once — a set names each purpose once',\n });\n }\n});\nexport type ServiceConsentRequirement = z.infer<typeof ServiceConsentRequirementSchema>;\n\n/**\n * WHO is publishing this notice, under what legal role, on what basis — the operator's own compliance\n * identity (issue #131).\n *\n * ## Why this exists, and why in configuration\n *\n * A produced compliance document asked three questions the application had nowhere to answer:\n * who operates the service, in what role, and on what lawful basis. Measured 2026-08-30, the only\n * `legalEntityName` anywhere in the compliance model was the PROCESSOR's, from the register in issue\n * #88 — nothing described the operator.\n *\n * That is not merely an empty field. A landing REPLACES the produced document on every run, so an\n * operator who answered by editing the document lost the answer on the next regeneration:\n * `.claude/rules/one-source-of-truth.md`'s test failing in the direction that matters — delete and\n * re-run, and you lose TRUTH rather than TIME.\n *\n * Configuration rather than a governed document, and identity and basis together rather than split.\n * They are two halves of ONE clause a reader meets in the first paragraph, and separating them would\n * put those halves in two places with two lifecycles for no benefit a reader can perceive. The\n * ASSESSMENT behind a basis is reviewed work and lives in `assessmentNote`; the conclusion belongs\n * with the identity that publishes it.\n *\n * ## What is deliberately NOT here\n *\n * A DPO appointment record, a processing-activities register (Art. 30), a retention schedule\n * (`dataRetention`), a processor register (#88). This is the operator's identity and legal posture —\n * the answers a notice needs in its opening sections — not a compliance programme.\n *\n * ## Every part is independently declarable, and that is the point (issue #228)\n *\n * This schema was optional AS A WHOLE and all-or-nothing INSIDE until 2026-09-02: the entity name,\n * address, contact, role allocations and lawful bases were each required, so an operator who knew\n * the registered name but had not settled lawful bases with counsel could record NOTHING. They\n * answered by editing the produced document, and a landing REPLACES it — losing the answer on the\n * next regeneration, which is the exact failure this schema was created to end.\n *\n * Paul's decision (#225) is that filling the entity must never block, and that generation is\n * resilient to what is missing while saying plainly that the result is incomplete. So every part is\n * optional and {@link outstandingOperatorComplianceIdentityParts} names what is still owed.\n *\n * What did NOT weaken: every refinement guarding a STATED claim. A legitimate-interests basis still\n * requires its assessment, and a non-empty required purpose set still requires what it gates. Those\n * refuse an INCOHERENT answer, never an ABSENT one, and the distinction is the whole design — a\n * partial declaration is honest, a self-contradicting one is not.\n */\nexport const ApplicationOperatorComplianceIdentitySchema = z.strictObject({\n /** The legal entity operating the service, as registered. Absent means unanswered — see the partial-filling note above. */\n legalEntityName: z.string().trim().min(1).optional(),\n /** Its registered address, as a reader would need it to serve a request. Absent means unanswered. */\n registeredAddress: z.string().trim().min(1).optional(),\n /**\n * How a data subject reaches the operator about privacy — an address, an email, or a URL.\n *\n * Open string because all three are legitimate and the law names none: what matters is that a route\n * exists and is stated, not what shape it takes.\n */\n privacyContact: z.string().trim().min(1).optional(),\n /**\n * An Art. 27 representative in the origin jurisdiction, where the operator is established outside it.\n *\n * Optional, because most operators are established where their subjects are and appointing one\n * would be a fiction. Absent means \"not required\", never \"not considered\" — a notice states it only\n * when declared.\n */\n representative: z.strictObject({\n name: z.string().trim().min(1),\n address: z.string().trim().min(1),\n }).optional(),\n /**\n * The operator's role, per population of data. At least one.\n *\n * See {@link OperatorRoleAllocationSchema} for why this is a list rather than a field.\n */\n roleAllocations: z.array(OperatorRoleAllocationSchema).min(1).optional(),\n /**\n * The lawful basis relied on, per processing purpose. At least one.\n *\n * `min(1)` at the schema for the same reason the placement plan requires one decision: an identity\n * declaring no basis at all is a refusal wearing a declaration's clothes, and it would land as a\n * complete answer.\n */\n lawfulBases: z.array(OperatorLawfulBasisSchema).min(1).superRefine((bases, context) => {\n // A purpose ref names ONE purpose, or a recipient linking to it would be linked to two (#1709).\n const seen = new Set<string>();\n bases.forEach((entry, index) => {\n if (entry.purposeRef === undefined) return;\n if (seen.has(entry.purposeRef)) {\n context.addIssue({ code: 'custom', path: [index, 'purposeRef'], message: `purpose ref \"${entry.purposeRef}\" names more than one purpose` });\n }\n seen.add(entry.purposeRef);\n });\n }).optional(),\n /**\n * What a person must consent to in order to use the service at all (issue #139).\n *\n * OPTIONAL, and absent means UNANSWERED rather than an empty set. The distinction is the one this\n * whole lane turns on: a declared `requiredPurposes: []` is the operator saying nothing is gated on consent, while\n * absence is nobody having been asked, and a notice must not render the second as the first.\n */\n serviceConsentRequirement: ServiceConsentRequirementSchema.optional(),\n /**\n * How the operator confirms that a person exercising a data-subject right is that person, before acting.\n *\n * The one part of the rights clause the law leaves to the operator: WHICH rights apply follows from the\n * jurisdictions and the lawful bases, and the complaint route from the regime, but the verification step\n * is a procedure the operator runs (GDPR Art. 12(6) lets a controller ask for what it reasonably needs).\n * Prose, because procedures differ — \"requests from a signed-in account are verified by that session;\n * requests by email are answered at the account's address\". Absent means undecided, and the\n * `rights-and-complaint-route` fact then answers nothing (#454).\n */\n subjectRequestIdentityVerification: z.string().trim().min(1).optional(),\n /**\n * Where the operator's OWN infrastructure hosts and processes the application's data — \"France\", \"eu-west-3\",\n * \"Germany (primary), Netherlands (backups)\". At least one when declared.\n *\n * ## Why it is here, and why nothing else answered it\n *\n * Every other region in the compliance model belongs to a THIRD party: `providerComplianceChoices.<ref>.processingRegion`\n * and the processor register's `processingRegions` say where a vendor processes. Nothing said where the application\n * itself runs (#2238), so a creation run could only carry it as a free-text interview answer (\"hosting provider and\n * exact EU/EEA region: not yet decided\"), which no derivation can read and the launch list could not name. Where the\n * operator hosts is the first thing an international-transfer statement needs, and it is the operator's decision about\n * its own product, so it sits with the operator's identity.\n *\n * Free text per region, like the processor register's `processingRegions`: a cloud region id and a country are both\n * answers a reader can act on, and the law names neither. A hosting company that processes on the operator's behalf is\n * ALSO a processor and belongs in the processor register; this field says where the data is, not who holds it.\n *\n * ## Rejected: deriving it from `remoteProvider.region`\n *\n * Each remote environment's `wildo.infra.remote.config.ts` may carry `remoteProvider.region` (`WildoRemoteProviderSchema`).\n * It is not this statement and is not read as one: it is absent at creation time (no environment exists yet), it is per\n * ENVIRONMENT where a notice states one answer for the production data, it describes the cluster and its registry rather\n * than everywhere the data lands (managed databases, object storage, backups), and it is deployment metadata nobody\n * reviews as a statement to a data subject. Two declarations of \"where we run\" can disagree; this one is the one a notice\n * quotes, and an operator who keeps both should keep them consistent.\n *\n * Absent means undecided, never \"hosted nowhere\".\n */\n hostingRegions: z.array(z.string().trim().min(1)).min(1).optional(),\n}).superRefine((identity, context) => {\n /*\n * A declaration with nothing in it is a no-op that reads as an answer.\n *\n * Every part is independently optional so an operator can record the half they know (#228), and the\n * cost of that is a new shape the old schema could not express: `operatorComplianceIdentity: {}`.\n * Absent and empty must not mean the same thing — absence is \"nobody was asked\", which every\n * consumer already renders as outstanding, while an empty object would satisfy the field and\n * disclose nothing. Same defect the file refuses twice already, for a legitimate-interests basis\n * with no assessment and a consent gate that names nothing.\n */\n if (Object.values(identity).every((part) => part === undefined)) {\n context.addIssue({\n code: 'custom',\n path: [],\n message:\n 'an operator compliance identity that declares nothing is not a partial answer, it is an empty one — '\n + 'record the part you know, or omit `operatorComplianceIdentity` entirely so it reads as unanswered',\n });\n }\n});\nexport type ApplicationOperatorComplianceIdentity = z.infer<typeof ApplicationOperatorComplianceIdentitySchema>;\n\n\n/**\n * The parts of an operator's compliance identity, so \"what is still owed\" has one named home.\n *\n * A closed vocabulary rather than free strings: every consumer that reports an incomplete identity —\n * a drafter's brief, a document's marked gap, an operator-facing check — must name the same parts by\n * the same names, or two surfaces tell an operator to fill two different things.\n *\n * `representative` and `serviceConsentRequirement` are deliberately absent from this list. Both are\n * legitimately unset on a COMPLETE identity: an operator established where its subjects are needs no\n * Art. 27 representative, and a declared absence of a consent gate is expressed by\n * `serviceConsentRequirement.requiredPurposes = []`, which is a value rather than an omission. Listing\n * them would report every correct identity as incomplete.\n */\nexport enum OperatorComplianceIdentityPart {\n LEGAL_ENTITY_NAME = 'legalEntityName',\n REGISTERED_ADDRESS = 'registeredAddress',\n PRIVACY_CONTACT = 'privacyContact',\n ROLE_ALLOCATIONS = 'roleAllocations',\n LAWFUL_BASES = 'lawfulBases',\n SUBJECT_REQUEST_IDENTITY_VERIFICATION = 'subjectRequestIdentityVerification',\n /** Where the operator's own infrastructure hosts the data (#2238). Owed by every operator: an application is hosted somewhere. */\n HOSTING_REGIONS = 'hostingRegions',\n}\n\n/**\n * Which parts of the identity are still owed, in declaration order.\n *\n * Empty means complete. An UNDECLARED identity is not this function's case — pass it and you get every\n * part back, which is correct but says less than the caller needs: \"nobody has been asked\" and \"four of\n * five parts are outstanding\" are different things to tell an operator, and the caller distinguishes\n * them by checking for the identity itself first.\n */\nexport function outstandingOperatorComplianceIdentityParts(\n identity: ApplicationOperatorComplianceIdentity | undefined,\n): readonly OperatorComplianceIdentityPart[] {\n return [\n identity?.legalEntityName === undefined ? OperatorComplianceIdentityPart.LEGAL_ENTITY_NAME : undefined,\n identity?.registeredAddress === undefined ? OperatorComplianceIdentityPart.REGISTERED_ADDRESS : undefined,\n identity?.privacyContact === undefined ? OperatorComplianceIdentityPart.PRIVACY_CONTACT : undefined,\n identity?.roleAllocations === undefined ? OperatorComplianceIdentityPart.ROLE_ALLOCATIONS : undefined,\n identity?.lawfulBases === undefined ? OperatorComplianceIdentityPart.LAWFUL_BASES : undefined,\n identity?.subjectRequestIdentityVerification === undefined ? OperatorComplianceIdentityPart.SUBJECT_REQUEST_IDENTITY_VERIFICATION : undefined,\n identity?.hostingRegions === undefined ? OperatorComplianceIdentityPart.HOSTING_REGIONS : undefined,\n ].filter((part): part is OperatorComplianceIdentityPart => part !== undefined);\n}\n\n/**\n * The lawful bases an operator declares, deduplicated — what a notice enumerates when it says which\n * bases the service relies on.\n *\n * `undefined` when none are declared, and NOT an empty array. A notice rendering an empty list would\n * say the service relies on no lawful basis, which is a claim nobody made and one no processing could\n * survive; the caller has to meet the absent case rather than fall into it.\n */\nexport function declaredLawfulBases(identity: ApplicationOperatorComplianceIdentity): readonly LawfulBasis[] | undefined {\n if (identity.lawfulBases === undefined) return undefined;\n const order = Object.values(LawfulBasis);\n return [...new Set(identity.lawfulBases.map((entry) => entry.basis))]\n .sort((left, right) => order.indexOf(left) - order.indexOf(right));\n}\n\n/**\n * True when the operator declares more than one role, so a notice must not assign a single one.\n *\n * `undefined` when no roles are declared — the question cannot be answered, and `false` would be read\n * as \"one role\", licensing exactly the collapse {@link OperatorRoleAllocationSchema} exists to prevent.\n */\nexport function operatorHoldsSeveralProcessingRoles(identity: ApplicationOperatorComplianceIdentity): boolean | undefined {\n if (identity.roleAllocations === undefined) return undefined;\n return new Set(identity.roleAllocations.map((allocation) => allocation.role)).size > 1;\n}\n"]}
|
|
@@ -35,7 +35,13 @@ export declare enum ApplicationRiskSignalRef {
|
|
|
35
35
|
/** Resources that have not answered the personal-data question at all. */
|
|
36
36
|
PERSONAL_DATA_DECLARATION_GAP = "application:personal-data-declaration-gap",
|
|
37
37
|
/** Operations reachable by an external agent over MCP. */
|
|
38
|
-
EXTERNAL_EXPOSURE_MCP = "application:external-exposure-mcp"
|
|
38
|
+
EXTERNAL_EXPOSURE_MCP = "application:external-exposure-mcp",
|
|
39
|
+
/**
|
|
40
|
+
* Where the brand's chosen NAME stands against its minimal trademark screening: cleared, screened
|
|
41
|
+
* incomplete, kept despite a live conflicting mark or named competitor, or never screened. A fact about the
|
|
42
|
+
* application's own declarations (its brand and its screening), downstream of a choice its creator made.
|
|
43
|
+
*/
|
|
44
|
+
BRAND_NAME_TRADEMARK_STANDING = "application:brand-name-trademark-standing"
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
41
47
|
* What an operator has DECIDED to do about an exposure.
|
|
@@ -133,10 +139,16 @@ export type ApplicationRiskPostureEntry = z.infer<typeof ApplicationRiskPostureE
|
|
|
133
139
|
* {@link undeclaredRiskSignals}, which is the "mark it, do not block" shape that rule asks for.
|
|
134
140
|
*
|
|
135
141
|
* So absence is representable and means one thing — nobody has decided yet — and it is
|
|
136
|
-
* distinguishable from `ACCEPTED`, which means somebody decided to live with it.
|
|
142
|
+
* distinguishable from `ACCEPTED`, which means somebody decided to live with it.
|
|
143
|
+
*
|
|
144
|
+
* `z.partialRecord`, NOT `z.record`: in Zod 4 a record keyed by an enum is EXHAUSTIVE — every member is
|
|
145
|
+
* required, at parse and in the inferred type. That contradicted this contract silently while every
|
|
146
|
+
* application declared every signal, and broke the day a signal was added
|
|
147
|
+
* (`BRAND_NAME_TRADEMARK_STANDING`): Wonder Todos' six-entry posture stopped parsing. A new signal must
|
|
148
|
+
* read as UNDECLARED in every existing application, never refuse it. Collapsing those two
|
|
137
149
|
* is the failure this layer exists to avoid: silence is not acceptance.
|
|
138
150
|
*/
|
|
139
|
-
export declare const ApplicationRiskPostureSchema: z.ZodRecord<z.ZodEnum<typeof ApplicationRiskSignalRef
|
|
151
|
+
export declare const ApplicationRiskPostureSchema: z.ZodRecord<z.ZodEnum<typeof ApplicationRiskSignalRef> & z.core.$partial, z.ZodObject<{
|
|
140
152
|
posture: z.ZodEnum<typeof RiskTreatmentPosture>;
|
|
141
153
|
rationale: z.ZodString;
|
|
142
154
|
}, z.core.$strict>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"risk-posture.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/compliance/risk-posture.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,oBAAY,wBAAwB;IAClC,oGAAoG;IACpG,kBAAkB,mCAAmC;IACrD,kFAAkF;IAClF,qBAAqB,sCAAsC;IAC3D,qFAAqF;IACrF,gBAAgB,iCAAiC;IACjD,oHAAoH;IACpH,uBAAuB,wCAAwC;IAC/D,0EAA0E;IAC1E,6BAA6B,8CAA8C;IAC3E,0DAA0D;IAC1D,qBAAqB,sCAAsC;
|
|
1
|
+
{"version":3,"file":"risk-posture.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/compliance/risk-posture.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,oBAAY,wBAAwB;IAClC,oGAAoG;IACpG,kBAAkB,mCAAmC;IACrD,kFAAkF;IAClF,qBAAqB,sCAAsC;IAC3D,qFAAqF;IACrF,gBAAgB,iCAAiC;IACjD,oHAAoH;IACpH,uBAAuB,wCAAwC;IAC/D,0EAA0E;IAC1E,6BAA6B,8CAA8C;IAC3E,0DAA0D;IAC1D,qBAAqB,sCAAsC;IAC3D;;;;OAIG;IACH,6BAA6B,8CAA8C;CAC5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,oBAAY,oBAAoB;IAC9B;;;;OAIG;IACH,OAAO,YAAY;IACnB,yEAAyE;IACzE,OAAO,YAAY;IACnB;;;;;;;OAOG;IACH,kBAAkB,uBAAuB;IACzC,kDAAkD;IAClD,QAAQ,aAAa;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iCAAiC;;;kBAO5C,CAAC;AAEH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAE5F;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,4BAA4B;;;mBAGxC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAAG,SAAS,GAC1C,SAAS,wBAAwB,EAAE,CAErC;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,2BAA2B,GAAG,SAAS,GAAG,OAAO,CAEvG"}
|
|
@@ -37,6 +37,12 @@ export var ApplicationRiskSignalRef;
|
|
|
37
37
|
ApplicationRiskSignalRef["PERSONAL_DATA_DECLARATION_GAP"] = "application:personal-data-declaration-gap";
|
|
38
38
|
/** Operations reachable by an external agent over MCP. */
|
|
39
39
|
ApplicationRiskSignalRef["EXTERNAL_EXPOSURE_MCP"] = "application:external-exposure-mcp";
|
|
40
|
+
/**
|
|
41
|
+
* Where the brand's chosen NAME stands against its minimal trademark screening: cleared, screened
|
|
42
|
+
* incomplete, kept despite a live conflicting mark or named competitor, or never screened. A fact about the
|
|
43
|
+
* application's own declarations (its brand and its screening), downstream of a choice its creator made.
|
|
44
|
+
*/
|
|
45
|
+
ApplicationRiskSignalRef["BRAND_NAME_TRADEMARK_STANDING"] = "application:brand-name-trademark-standing";
|
|
40
46
|
})(ApplicationRiskSignalRef || (ApplicationRiskSignalRef = {}));
|
|
41
47
|
/**
|
|
42
48
|
* What an operator has DECIDED to do about an exposure.
|
|
@@ -138,10 +144,16 @@ export const ApplicationRiskPostureEntrySchema = z.strictObject({
|
|
|
138
144
|
* {@link undeclaredRiskSignals}, which is the "mark it, do not block" shape that rule asks for.
|
|
139
145
|
*
|
|
140
146
|
* So absence is representable and means one thing — nobody has decided yet — and it is
|
|
141
|
-
* distinguishable from `ACCEPTED`, which means somebody decided to live with it.
|
|
147
|
+
* distinguishable from `ACCEPTED`, which means somebody decided to live with it.
|
|
148
|
+
*
|
|
149
|
+
* `z.partialRecord`, NOT `z.record`: in Zod 4 a record keyed by an enum is EXHAUSTIVE — every member is
|
|
150
|
+
* required, at parse and in the inferred type. That contradicted this contract silently while every
|
|
151
|
+
* application declared every signal, and broke the day a signal was added
|
|
152
|
+
* (`BRAND_NAME_TRADEMARK_STANDING`): Wonder Todos' six-entry posture stopped parsing. A new signal must
|
|
153
|
+
* read as UNDECLARED in every existing application, never refuse it. Collapsing those two
|
|
142
154
|
* is the failure this layer exists to avoid: silence is not acceptance.
|
|
143
155
|
*/
|
|
144
|
-
export const ApplicationRiskPostureSchema = z.
|
|
156
|
+
export const ApplicationRiskPostureSchema = z.partialRecord(z.enum(ApplicationRiskSignalRef), ApplicationRiskPostureEntrySchema);
|
|
145
157
|
/**
|
|
146
158
|
* The signals an application has declared NO posture for — what a register must mark as owed.
|
|
147
159
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"risk-posture.shared.schemas.js","sourceRoot":"","sources":["../../../../src/compliance/risk-posture.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAN,IAAY,wBAaX;AAbD,WAAY,wBAAwB;IAClC,oGAAoG;IACpG,iFAAqD,CAAA;IACrD,kFAAkF;IAClF,uFAA2D,CAAA;IAC3D,qFAAqF;IACrF,6EAAiD,CAAA;IACjD,oHAAoH;IACpH,2FAA+D,CAAA;IAC/D,0EAA0E;IAC1E,uGAA2E,CAAA;IAC3E,0DAA0D;IAC1D,uFAA2D,CAAA;AAC7D,CAAC,EAbW,wBAAwB,KAAxB,wBAAwB,QAanC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,CAAN,IAAY,oBAoBX;AApBD,WAAY,oBAAoB;IAC9B;;;;OAIG;IACH,2CAAmB,CAAA;IACnB,yEAAyE;IACzE,2CAAmB,CAAA;IACnB;;;;;;;OAOG;IACH,iEAAyC,CAAA;IACzC,kDAAkD;IAClD,6CAAqB,CAAA;AACvB,CAAC,EApBW,oBAAoB,KAApB,oBAAoB,QAoB/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9D,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,sDAAsD,CAAC;IACtG,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kHAAkH,CAAC;CAChI,CAAC,CAAC;AAIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAClD,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAChC,iCAAiC,CAClC,CAAC;AAIF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAA2C;IAE3C,OAAO,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC;AAC3G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAAC,KAA8C;IAC5F,OAAO,KAAK,EAAE,OAAO,KAAK,oBAAoB,CAAC,OAAO,CAAC;AACzD,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * The risk signals an APPLICATION may take a posture on (#300 Layer 2).\n *\n * ## Why only the application-evaluation signals\n *\n * `report-risk-signals.mjs` derives signals in two homes, and the split is load-bearing rather than\n * organisational. FRAMEWORK_BUILD_TIME signals — marker density across the objective catalogue,\n * objectives with no marker, objectives marked only outside production — are facts about the ENGINE\n * and are identical for every application. An application cannot change them and does not choose\n * them, so a declared posture about one would be an operator accepting somebody else's exposure.\n * They inform; they are not accepted.\n *\n * APPLICATION_EVALUATION_TIME signals are facts about one application's own declarations, and every\n * one of them is downstream of a choice its operator made. Those are the ones a posture is about.\n *\n * ## This enum is the SOURCE, and the derivation is held to it\n *\n * The refs were string literals inside the report. A second list here, free to drift from the one the\n * script emits, is exactly the transcription defect the assurance anchoring rule exists to stop — so\n * the report now parses this enum and refuses when the set it emits and the set declared here\n * disagree, in EITHER direction. A signal added to the report with no member here fails; a member\n * here that no signal emits fails too, because an application could otherwise declare a posture\n * about an exposure nothing measures.\n */\nexport enum ApplicationRiskSignalRef {\n /** Which engine capabilities are enabled, and therefore which exposures are structurally absent. */\n CAPABILITY_SURFACE = 'application:capability-surface',\n /** Whether every retention class the vocabulary carries has a declared period. */\n RETENTION_DECLARATION = 'application:retention-declaration',\n /** How many configured providers are vendor-hosted, each a third-party processor. */\n PROVIDER_HOSTING = 'application:provider-hosting',\n /** Which personal-data categories this application's own resources declare, and whether any is special category. */\n PERSONAL_DATA_INVENTORY = 'application:personal-data-inventory',\n /** Resources that have not answered the personal-data question at all. */\n PERSONAL_DATA_DECLARATION_GAP = 'application:personal-data-declaration-gap',\n /** Operations reachable by an external agent over MCP. */\n EXTERNAL_EXPOSURE_MCP = 'application:external-exposure-mcp',\n}\n\n/**\n * What an operator has DECIDED to do about an exposure.\n *\n * NIST SP 800-39 names the risk responses as accepting, avoiding, mitigating, sharing or\n * transferring risk, and that set is what this enum takes — the options, not the spelling.\n * spec-cite: NIST SP 800-39 / CSRC glossary, \"risk response\" — paraphrased from the published\n * definition, not quoted.\n *\n * ## Only NIST is NAMED here, and that is a licensing line rather than a preference\n *\n * Other risk-management standards describe the same options and are LICENSED documents. Naming one\n * in engine source would put a paid standard's identity into shipped code, which is why this\n * repository's boundary rule keeps external framework names to rights-classified mapping and report\n * surfaces. NIST publications are US government works, so citing one here costs nothing — and the\n * practice is already settled: NIST is named in 34 engine and platform files.\n *\n * The options are borrowed either way. Being INSPIRED by a standard and NAMING it are different\n * acts, and only the second carries a licence.\n *\n * ## No band, no score, no number — and that is inherited rather than chosen here\n *\n * Layer 1 emits signals carrying NO score, weight, level or severity, and asserts that prohibition\n * structurally. A posture that arrived as a 1–5 appetite rating would put a number underneath a layer\n * that refuses to produce one, and the number is what a reader trusts without checking. What an\n * operator can honestly say about an exposure is which of these four they chose and why, so that is\n * what the declaration carries.\n *\n * assurance-control: WILDO.RISK.TREATMENT_DECLARED — an operator's chosen treatment per exposure,\n * with the reasoning, in a form a measurement can contradict.\n *\n * ## `AVOIDED` is the member that makes this layer worth having\n *\n * The other three are consistent with any measurement: an application that REDUCED, SHARED or\n * ACCEPTED an exposure still has it, so the signal and the posture agree whatever the signal says.\n * `AVOIDED` claims the exposure is not taken at all, which a Layer 1 signal can CONTRADICT — declare\n * that vendor-hosted processing is avoided while two of three configured providers are vendor-hosted,\n * and the two statements cannot both be true.\n *\n * That is the difference between this and a questionnaire answer. A free-text \"describe your risk\n * appetite\" is unfalsifiable by construction; a typed posture beside a derived signal is a claim the\n * product itself can disagree with.\n */\nexport enum RiskTreatmentPosture {\n /**\n * The application does not take this exposure at all.\n *\n * The only FALSIFIABLE posture — a signal reporting the exposure present contradicts it directly.\n */\n AVOIDED = 'AVOIDED',\n /** Taken, with controls that lower its likelihood or its consequence. */\n REDUCED = 'REDUCED',\n /**\n * Taken, with the consequence placed on another party by CONTRACT — an Art. 28 processor agreement\n * being the case this framework can actually evidence.\n *\n * Narrower than \"share or transfer\" on purpose. Insurance-style transfer is a legitimate treatment\n * and nothing in the product can attest it, so modelling it here would add a member no derivation\n * could ever check. Naming the contractual case precisely is a smaller claim and a true one.\n */\n SHARED_BY_CONTRACT = 'SHARED_BY_CONTRACT',\n /** Taken, and the exposure retained knowingly. */\n ACCEPTED = 'ACCEPTED',\n}\n\n/**\n * One signal's posture, and why.\n *\n * `rationale` is REQUIRED, for the same reason `dataRetention` requires one: a posture with no reason\n * is a token, and the thing a reviewer needs is the sentence, not the enum member. \"We accept it\" is\n * what the member already says.\n *\n * ## What is deliberately NOT here\n *\n * **No `decidedBy` and no `decidedAt`.** Acceptance is a COMMIT — `one-source-of-truth.md` rule 7 and\n * D5 — so the author and the instant are already recorded, mergeably and by a history everyone\n * audits. Fields restating them would be a second authority that can disagree with git, and they are\n * exactly the shape that made an earlier compliance record unproducible without forging an identity.\n *\n * **No review date.** `derive-before-you-ask.md` settles this: a date is a PROXY for \"an input may\n * have changed\", every input is in git and enumerable, so the trigger is computable and the date is\n * an output rather than something to type.\n */\nexport const ApplicationRiskPostureEntrySchema = z.strictObject({\n posture: z.enum(RiskTreatmentPosture).describe('What the operator decided to do about this exposure.'),\n rationale: z\n .string()\n .trim()\n .min(1)\n .describe('Why this posture, in the operator\\'s own words — the sentence a reviewer reads, not a restatement of the member.'),\n});\n\nexport type ApplicationRiskPostureEntry = z.infer<typeof ApplicationRiskPostureEntrySchema>;\n\n/**\n * An application's declared posture per risk signal — PARTIAL by construction.\n *\n * Every entry is optional and the whole declaration may be omitted. That is not laxity: a compliance\n * workflow may never stand in front of the mechanism (`application-creation-directive.md`), and a\n * required posture would make an application UNBUILDABLE until somebody decided something about\n * every signal the framework happens to derive. The undeclared set is REPORTED instead, by\n * {@link undeclaredRiskSignals}, which is the \"mark it, do not block\" shape that rule asks for.\n *\n * So absence is representable and means one thing — nobody has decided yet — and it is\n * distinguishable from `ACCEPTED`, which means somebody decided to live with it. Collapsing those two\n * is the failure this layer exists to avoid: silence is not acceptance.\n */\nexport const ApplicationRiskPostureSchema = z.record(\n z.enum(ApplicationRiskSignalRef),\n ApplicationRiskPostureEntrySchema,\n);\n\nexport type ApplicationRiskPosture = z.infer<typeof ApplicationRiskPostureSchema>;\n\n/**\n * The signals an application has declared NO posture for — what a register must mark as owed.\n *\n * Derived rather than stored, and total over the enum, so a signal added to the vocabulary tomorrow\n * is reported as undeclared by every existing application rather than silently omitted from the\n * answer. Deliberately the same shape as `undeclaredRetentionClasses`, because it is the same\n * question one layer over.\n */\nexport function undeclaredRiskSignals(\n posture: ApplicationRiskPosture | undefined,\n): readonly ApplicationRiskSignalRef[] {\n return Object.values(ApplicationRiskSignalRef).filter((signalRef) => posture?.[signalRef] === undefined);\n}\n\n/**\n * Postures that CLAIM AN EXPOSURE IS NOT TAKEN — the ones a measurement can contradict.\n *\n * Exported rather than inlined at the one call site, so the report and any later consumer ask the\n * same question. Today it is `AVOIDED` alone; if a future member is also falsifiable it joins here\n * and every consumer follows, which is the whole reason this is a function and not a comparison.\n */\nexport function riskPostureClaimsExposureAbsent(entry: ApplicationRiskPostureEntry | undefined): boolean {\n return entry?.posture === RiskTreatmentPosture.AVOIDED;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"risk-posture.shared.schemas.js","sourceRoot":"","sources":["../../../../src/compliance/risk-posture.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAN,IAAY,wBAmBX;AAnBD,WAAY,wBAAwB;IAClC,oGAAoG;IACpG,iFAAqD,CAAA;IACrD,kFAAkF;IAClF,uFAA2D,CAAA;IAC3D,qFAAqF;IACrF,6EAAiD,CAAA;IACjD,oHAAoH;IACpH,2FAA+D,CAAA;IAC/D,0EAA0E;IAC1E,uGAA2E,CAAA;IAC3E,0DAA0D;IAC1D,uFAA2D,CAAA;IAC3D;;;;OAIG;IACH,uGAA2E,CAAA;AAC7E,CAAC,EAnBW,wBAAwB,KAAxB,wBAAwB,QAmBnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,CAAN,IAAY,oBAoBX;AApBD,WAAY,oBAAoB;IAC9B;;;;OAIG;IACH,2CAAmB,CAAA;IACnB,yEAAyE;IACzE,2CAAmB,CAAA;IACnB;;;;;;;OAOG;IACH,iEAAyC,CAAA;IACzC,kDAAkD;IAClD,6CAAqB,CAAA;AACvB,CAAC,EApBW,oBAAoB,KAApB,oBAAoB,QAoB/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,YAAY,CAAC;IAC9D,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,sDAAsD,CAAC;IACtG,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kHAAkH,CAAC;CAChI,CAAC,CAAC;AAIH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,aAAa,CACzD,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAChC,iCAAiC,CAClC,CAAC;AAIF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAA2C;IAE3C,OAAO,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC;AAC3G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAAC,KAA8C;IAC5F,OAAO,KAAK,EAAE,OAAO,KAAK,oBAAoB,CAAC,OAAO,CAAC;AACzD,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * The risk signals an APPLICATION may take a posture on (#300 Layer 2).\n *\n * ## Why only the application-evaluation signals\n *\n * `report-risk-signals.mjs` derives signals in two homes, and the split is load-bearing rather than\n * organisational. FRAMEWORK_BUILD_TIME signals — marker density across the objective catalogue,\n * objectives with no marker, objectives marked only outside production — are facts about the ENGINE\n * and are identical for every application. An application cannot change them and does not choose\n * them, so a declared posture about one would be an operator accepting somebody else's exposure.\n * They inform; they are not accepted.\n *\n * APPLICATION_EVALUATION_TIME signals are facts about one application's own declarations, and every\n * one of them is downstream of a choice its operator made. Those are the ones a posture is about.\n *\n * ## This enum is the SOURCE, and the derivation is held to it\n *\n * The refs were string literals inside the report. A second list here, free to drift from the one the\n * script emits, is exactly the transcription defect the assurance anchoring rule exists to stop — so\n * the report now parses this enum and refuses when the set it emits and the set declared here\n * disagree, in EITHER direction. A signal added to the report with no member here fails; a member\n * here that no signal emits fails too, because an application could otherwise declare a posture\n * about an exposure nothing measures.\n */\nexport enum ApplicationRiskSignalRef {\n /** Which engine capabilities are enabled, and therefore which exposures are structurally absent. */\n CAPABILITY_SURFACE = 'application:capability-surface',\n /** Whether every retention class the vocabulary carries has a declared period. */\n RETENTION_DECLARATION = 'application:retention-declaration',\n /** How many configured providers are vendor-hosted, each a third-party processor. */\n PROVIDER_HOSTING = 'application:provider-hosting',\n /** Which personal-data categories this application's own resources declare, and whether any is special category. */\n PERSONAL_DATA_INVENTORY = 'application:personal-data-inventory',\n /** Resources that have not answered the personal-data question at all. */\n PERSONAL_DATA_DECLARATION_GAP = 'application:personal-data-declaration-gap',\n /** Operations reachable by an external agent over MCP. */\n EXTERNAL_EXPOSURE_MCP = 'application:external-exposure-mcp',\n /**\n * Where the brand's chosen NAME stands against its minimal trademark screening: cleared, screened\n * incomplete, kept despite a live conflicting mark or named competitor, or never screened. A fact about the\n * application's own declarations (its brand and its screening), downstream of a choice its creator made.\n */\n BRAND_NAME_TRADEMARK_STANDING = 'application:brand-name-trademark-standing',\n}\n\n/**\n * What an operator has DECIDED to do about an exposure.\n *\n * NIST SP 800-39 names the risk responses as accepting, avoiding, mitigating, sharing or\n * transferring risk, and that set is what this enum takes — the options, not the spelling.\n * spec-cite: NIST SP 800-39 / CSRC glossary, \"risk response\" — paraphrased from the published\n * definition, not quoted.\n *\n * ## Only NIST is NAMED here, and that is a licensing line rather than a preference\n *\n * Other risk-management standards describe the same options and are LICENSED documents. Naming one\n * in engine source would put a paid standard's identity into shipped code, which is why this\n * repository's boundary rule keeps external framework names to rights-classified mapping and report\n * surfaces. NIST publications are US government works, so citing one here costs nothing — and the\n * practice is already settled: NIST is named in 34 engine and platform files.\n *\n * The options are borrowed either way. Being INSPIRED by a standard and NAMING it are different\n * acts, and only the second carries a licence.\n *\n * ## No band, no score, no number — and that is inherited rather than chosen here\n *\n * Layer 1 emits signals carrying NO score, weight, level or severity, and asserts that prohibition\n * structurally. A posture that arrived as a 1–5 appetite rating would put a number underneath a layer\n * that refuses to produce one, and the number is what a reader trusts without checking. What an\n * operator can honestly say about an exposure is which of these four they chose and why, so that is\n * what the declaration carries.\n *\n * assurance-control: WILDO.RISK.TREATMENT_DECLARED — an operator's chosen treatment per exposure,\n * with the reasoning, in a form a measurement can contradict.\n *\n * ## `AVOIDED` is the member that makes this layer worth having\n *\n * The other three are consistent with any measurement: an application that REDUCED, SHARED or\n * ACCEPTED an exposure still has it, so the signal and the posture agree whatever the signal says.\n * `AVOIDED` claims the exposure is not taken at all, which a Layer 1 signal can CONTRADICT — declare\n * that vendor-hosted processing is avoided while two of three configured providers are vendor-hosted,\n * and the two statements cannot both be true.\n *\n * That is the difference between this and a questionnaire answer. A free-text \"describe your risk\n * appetite\" is unfalsifiable by construction; a typed posture beside a derived signal is a claim the\n * product itself can disagree with.\n */\nexport enum RiskTreatmentPosture {\n /**\n * The application does not take this exposure at all.\n *\n * The only FALSIFIABLE posture — a signal reporting the exposure present contradicts it directly.\n */\n AVOIDED = 'AVOIDED',\n /** Taken, with controls that lower its likelihood or its consequence. */\n REDUCED = 'REDUCED',\n /**\n * Taken, with the consequence placed on another party by CONTRACT — an Art. 28 processor agreement\n * being the case this framework can actually evidence.\n *\n * Narrower than \"share or transfer\" on purpose. Insurance-style transfer is a legitimate treatment\n * and nothing in the product can attest it, so modelling it here would add a member no derivation\n * could ever check. Naming the contractual case precisely is a smaller claim and a true one.\n */\n SHARED_BY_CONTRACT = 'SHARED_BY_CONTRACT',\n /** Taken, and the exposure retained knowingly. */\n ACCEPTED = 'ACCEPTED',\n}\n\n/**\n * One signal's posture, and why.\n *\n * `rationale` is REQUIRED, for the same reason `dataRetention` requires one: a posture with no reason\n * is a token, and the thing a reviewer needs is the sentence, not the enum member. \"We accept it\" is\n * what the member already says.\n *\n * ## What is deliberately NOT here\n *\n * **No `decidedBy` and no `decidedAt`.** Acceptance is a COMMIT — `one-source-of-truth.md` rule 7 and\n * D5 — so the author and the instant are already recorded, mergeably and by a history everyone\n * audits. Fields restating them would be a second authority that can disagree with git, and they are\n * exactly the shape that made an earlier compliance record unproducible without forging an identity.\n *\n * **No review date.** `derive-before-you-ask.md` settles this: a date is a PROXY for \"an input may\n * have changed\", every input is in git and enumerable, so the trigger is computable and the date is\n * an output rather than something to type.\n */\nexport const ApplicationRiskPostureEntrySchema = z.strictObject({\n posture: z.enum(RiskTreatmentPosture).describe('What the operator decided to do about this exposure.'),\n rationale: z\n .string()\n .trim()\n .min(1)\n .describe('Why this posture, in the operator\\'s own words — the sentence a reviewer reads, not a restatement of the member.'),\n});\n\nexport type ApplicationRiskPostureEntry = z.infer<typeof ApplicationRiskPostureEntrySchema>;\n\n/**\n * An application's declared posture per risk signal — PARTIAL by construction.\n *\n * Every entry is optional and the whole declaration may be omitted. That is not laxity: a compliance\n * workflow may never stand in front of the mechanism (`application-creation-directive.md`), and a\n * required posture would make an application UNBUILDABLE until somebody decided something about\n * every signal the framework happens to derive. The undeclared set is REPORTED instead, by\n * {@link undeclaredRiskSignals}, which is the \"mark it, do not block\" shape that rule asks for.\n *\n * So absence is representable and means one thing — nobody has decided yet — and it is\n * distinguishable from `ACCEPTED`, which means somebody decided to live with it.\n *\n * `z.partialRecord`, NOT `z.record`: in Zod 4 a record keyed by an enum is EXHAUSTIVE — every member is\n * required, at parse and in the inferred type. That contradicted this contract silently while every\n * application declared every signal, and broke the day a signal was added\n * (`BRAND_NAME_TRADEMARK_STANDING`): Wonder Todos' six-entry posture stopped parsing. A new signal must\n * read as UNDECLARED in every existing application, never refuse it. Collapsing those two\n * is the failure this layer exists to avoid: silence is not acceptance.\n */\nexport const ApplicationRiskPostureSchema = z.partialRecord(\n z.enum(ApplicationRiskSignalRef),\n ApplicationRiskPostureEntrySchema,\n);\n\nexport type ApplicationRiskPosture = z.infer<typeof ApplicationRiskPostureSchema>;\n\n/**\n * The signals an application has declared NO posture for — what a register must mark as owed.\n *\n * Derived rather than stored, and total over the enum, so a signal added to the vocabulary tomorrow\n * is reported as undeclared by every existing application rather than silently omitted from the\n * answer. Deliberately the same shape as `undeclaredRetentionClasses`, because it is the same\n * question one layer over.\n */\nexport function undeclaredRiskSignals(\n posture: ApplicationRiskPosture | undefined,\n): readonly ApplicationRiskSignalRef[] {\n return Object.values(ApplicationRiskSignalRef).filter((signalRef) => posture?.[signalRef] === undefined);\n}\n\n/**\n * Postures that CLAIM AN EXPOSURE IS NOT TAKEN — the ones a measurement can contradict.\n *\n * Exported rather than inlined at the one call site, so the report and any later consumer ask the\n * same question. Today it is `AVOIDED` alone; if a future member is also falsifiable it joins here\n * and every consumer follows, which is the whole reason this is a function and not a comparison.\n */\nexport function riskPostureClaimsExposureAbsent(entry: ApplicationRiskPostureEntry | undefined): boolean {\n return entry?.posture === RiskTreatmentPosture.AVOIDED;\n}\n"]}
|