@vertesia/common 1.6.0-dev.20260923.085325Z → 1.6.0-dev.20261008.064737Z
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/lib/access-control-values.d.ts +2 -2
- package/lib/access-control-values.d.ts.map +1 -1
- package/lib/access-control-values.js +2 -2
- package/lib/access-control-values.js.map +1 -1
- package/lib/access-control.d.ts +7 -8
- package/lib/access-control.d.ts.map +1 -1
- package/lib/access-control.js +5 -0
- package/lib/access-control.js.map +1 -1
- package/lib/analytics.d.ts +6 -7
- package/lib/analytics.d.ts.map +1 -1
- package/lib/api-contract/components.generated.json +2553 -1046
- package/lib/api-contract/engine.d.ts +65 -0
- package/lib/api-contract/engine.d.ts.map +1 -0
- package/lib/api-contract/engine.js +569 -0
- package/lib/api-contract/engine.js.map +1 -0
- package/lib/api-contract/index.d.ts +21 -182
- package/lib/api-contract/index.d.ts.map +1 -1
- package/lib/api-contract/index.js +13 -551
- package/lib/api-contract/index.js.map +1 -1
- package/lib/api-schemas/access-control.d.ts +33 -10
- package/lib/api-schemas/access-control.d.ts.map +1 -1
- package/lib/api-schemas/access-control.js +7 -6
- package/lib/api-schemas/access-control.js.map +1 -1
- package/lib/api-schemas/account.d.ts +5 -9
- package/lib/api-schemas/account.d.ts.map +1 -1
- package/lib/api-schemas/account.js +0 -7
- package/lib/api-schemas/account.js.map +1 -1
- package/lib/api-schemas/agent-budget.d.ts +15 -0
- package/lib/api-schemas/agent-budget.d.ts.map +1 -0
- package/lib/api-schemas/agent-budget.js +55 -0
- package/lib/api-schemas/agent-budget.js.map +1 -0
- package/lib/api-schemas/agent-runs.d.ts +1525 -100
- package/lib/api-schemas/agent-runs.d.ts.map +1 -1
- package/lib/api-schemas/agent-runs.js +60 -28
- package/lib/api-schemas/agent-runs.js.map +1 -1
- package/lib/api-schemas/apikey.d.ts +11 -10
- package/lib/api-schemas/apikey.d.ts.map +1 -1
- package/lib/api-schemas/apikey.js.map +1 -1
- package/lib/api-schemas/app-lifecycle.d.ts +13 -0
- package/lib/api-schemas/app-lifecycle.d.ts.map +1 -1
- package/lib/api-schemas/app-lifecycle.js +4 -0
- package/lib/api-schemas/app-lifecycle.js.map +1 -1
- package/lib/api-schemas/app-runtime.d.ts +1821 -108
- package/lib/api-schemas/app-runtime.d.ts.map +1 -1
- package/lib/api-schemas/audit-trail.d.ts +275 -0
- package/lib/api-schemas/audit-trail.d.ts.map +1 -1
- package/lib/api-schemas/audit-trail.js +156 -1
- package/lib/api-schemas/audit-trail.js.map +1 -1
- package/lib/api-schemas/content.d.ts +665 -30
- package/lib/api-schemas/content.d.ts.map +1 -1
- package/lib/api-schemas/content.js +47 -0
- package/lib/api-schemas/content.js.map +1 -1
- package/lib/api-schemas/cost-analytics.d.ts +19 -0
- package/lib/api-schemas/cost-analytics.d.ts.map +1 -1
- package/lib/api-schemas/cost-analytics.js +38 -2
- package/lib/api-schemas/cost-analytics.js.map +1 -1
- package/lib/api-schemas/document-processing.d.ts +381 -18
- package/lib/api-schemas/document-processing.d.ts.map +1 -1
- package/lib/api-schemas/environment.d.ts +0 -5
- package/lib/api-schemas/environment.d.ts.map +1 -1
- package/lib/api-schemas/environment.js +0 -1
- package/lib/api-schemas/environment.js.map +1 -1
- package/lib/api-schemas/events.d.ts +1143 -54
- package/lib/api-schemas/events.d.ts.map +1 -1
- package/lib/api-schemas/group.d.ts +2 -1
- package/lib/api-schemas/group.d.ts.map +1 -1
- package/lib/api-schemas/group.js.map +1 -1
- package/lib/api-schemas/index.d.ts +2 -0
- package/lib/api-schemas/index.d.ts.map +1 -1
- package/lib/api-schemas/index.js +2 -0
- package/lib/api-schemas/index.js.map +1 -1
- package/lib/api-schemas/inference-profile.d.ts +5017 -0
- package/lib/api-schemas/inference-profile.d.ts.map +1 -0
- package/lib/api-schemas/inference-profile.js +135 -0
- package/lib/api-schemas/inference-profile.js.map +1 -0
- package/lib/api-schemas/integrations.d.ts +6 -6
- package/lib/api-schemas/interaction.d.ts +34391 -703
- package/lib/api-schemas/interaction.d.ts.map +1 -1
- package/lib/api-schemas/interaction.js +34 -1
- package/lib/api-schemas/interaction.js.map +1 -1
- package/lib/api-schemas/invites.d.ts +10 -9
- package/lib/api-schemas/invites.d.ts.map +1 -1
- package/lib/api-schemas/invites.js.map +1 -1
- package/lib/api-schemas/process.d.ts +653 -34
- package/lib/api-schemas/process.d.ts.map +1 -1
- package/lib/api-schemas/process.js +66 -0
- package/lib/api-schemas/process.js.map +1 -1
- package/lib/api-schemas/project-configuration.d.ts +1323 -62
- package/lib/api-schemas/project-configuration.d.ts.map +1 -1
- package/lib/api-schemas/project-configuration.js +15 -1
- package/lib/api-schemas/project-configuration.js.map +1 -1
- package/lib/api-schemas/project.d.ts +2031 -116
- package/lib/api-schemas/project.d.ts.map +1 -1
- package/lib/api-schemas/project.js +9 -1
- package/lib/api-schemas/project.js.map +1 -1
- package/lib/api-schemas/quota.d.ts +7 -6
- package/lib/api-schemas/quota.d.ts.map +1 -1
- package/lib/api-schemas/quota.js.map +1 -1
- package/lib/api-schemas/registry.d.ts +28171 -36096
- package/lib/api-schemas/registry.d.ts.map +1 -1
- package/lib/api-schemas/registry.js +77 -30
- package/lib/api-schemas/registry.js.map +1 -1
- package/lib/api-schemas/run-budget.d.ts +16 -0
- package/lib/api-schemas/run-budget.d.ts.map +1 -0
- package/lib/api-schemas/run-budget.js +22 -0
- package/lib/api-schemas/run-budget.js.map +1 -0
- package/lib/api-schemas/secrets.d.ts +0 -71
- package/lib/api-schemas/secrets.d.ts.map +1 -1
- package/lib/api-schemas/secrets.js +0 -73
- package/lib/api-schemas/secrets.js.map +1 -1
- package/lib/api-schemas/store.d.ts +6388 -338
- package/lib/api-schemas/store.d.ts.map +1 -1
- package/lib/api-schemas/store.js +11 -0
- package/lib/api-schemas/store.js.map +1 -1
- package/lib/api-schemas/user.d.ts +9 -8
- package/lib/api-schemas/user.d.ts.map +1 -1
- package/lib/api-schemas/user.js.map +1 -1
- package/lib/api-schemas/view-execution.d.ts +8 -0
- package/lib/api-schemas/view-execution.d.ts.map +1 -1
- package/lib/api-schemas/views.d.ts +14 -0
- package/lib/api-schemas/views.d.ts.map +1 -1
- package/lib/api-schemas/workflow-runs.d.ts +335 -12
- package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
- package/lib/api-schemas/workflow-runs.js +2 -0
- package/lib/api-schemas/workflow-runs.js.map +1 -1
- package/lib/apikey.d.ts +30 -25
- package/lib/apikey.d.ts.map +1 -1
- package/lib/apikey.js.map +1 -1
- package/lib/apps.d.ts +116 -121
- package/lib/apps.d.ts.map +1 -1
- package/lib/apps.js.map +1 -1
- package/lib/audit-trail.d.ts +30 -19
- package/lib/audit-trail.d.ts.map +1 -1
- package/lib/audit-trail.js +6 -0
- package/lib/audit-trail.js.map +1 -1
- package/lib/browser-credentials.d.ts +11 -12
- package/lib/browser-credentials.d.ts.map +1 -1
- package/lib/common.d.ts +17 -24
- package/lib/common.d.ts.map +1 -1
- package/lib/content-query.d.ts +3 -4
- package/lib/content-query.d.ts.map +1 -1
- package/lib/cost-analytics.d.ts +13 -21
- package/lib/cost-analytics.d.ts.map +1 -1
- package/lib/cost-analytics.js +0 -7
- package/lib/cost-analytics.js.map +1 -1
- package/lib/data-platform.d.ts +79 -88
- package/lib/data-platform.d.ts.map +1 -1
- package/lib/data-platform.js.map +1 -1
- package/lib/delegation.d.ts +4 -5
- package/lib/delegation.d.ts.map +1 -1
- package/lib/email.d.ts +15 -17
- package/lib/email.d.ts.map +1 -1
- package/lib/email.js.map +1 -1
- package/lib/embeddings.d.ts +25 -26
- package/lib/embeddings.d.ts.map +1 -1
- package/lib/environment.d.ts +16 -17
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js.map +1 -1
- package/lib/facets.d.ts +3 -4
- package/lib/facets.d.ts.map +1 -1
- package/lib/group.d.ts +6 -7
- package/lib/group.d.ts.map +1 -1
- package/lib/group.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/inference-profile.d.ts +17 -0
- package/lib/inference-profile.d.ts.map +1 -0
- package/lib/inference-profile.js +2 -0
- package/lib/inference-profile.js.map +1 -0
- package/lib/integrations.d.ts +19 -20
- package/lib/integrations.d.ts.map +1 -1
- package/lib/integrations.js.map +1 -1
- package/lib/interaction.d.ts +68 -73
- package/lib/interaction.d.ts.map +1 -1
- package/lib/interaction.js.map +1 -1
- package/lib/meters.d.ts +4 -5
- package/lib/meters.d.ts.map +1 -1
- package/lib/oauth-server.d.ts +40 -41
- package/lib/oauth-server.d.ts.map +1 -1
- package/lib/oauth.d.ts +9 -10
- package/lib/oauth.d.ts.map +1 -1
- package/lib/payload.d.ts +11 -14
- package/lib/payload.d.ts.map +1 -1
- package/lib/pending-asks.d.ts +8 -13
- package/lib/pending-asks.d.ts.map +1 -1
- package/lib/pending-asks.js +0 -4
- package/lib/pending-asks.js.map +1 -1
- package/lib/platform-event.d.ts +48 -71
- package/lib/platform-event.d.ts.map +1 -1
- package/lib/platform-event.js.map +1 -1
- package/lib/principal-context.d.ts +3 -3
- package/lib/principal-context.d.ts.map +1 -1
- package/lib/project.d.ts +42 -43
- package/lib/project.d.ts.map +1 -1
- package/lib/project.js.map +1 -1
- package/lib/prompt.d.ts +14 -16
- package/lib/prompt.d.ts.map +1 -1
- package/lib/prompt.js.map +1 -1
- package/lib/query.d.ts +10 -13
- package/lib/query.d.ts.map +1 -1
- package/lib/rate-limiter.d.ts +7 -11
- package/lib/rate-limiter.d.ts.map +1 -1
- package/lib/refs.d.ts +2 -3
- package/lib/refs.d.ts.map +1 -1
- package/lib/refs.js.map +1 -1
- package/lib/roles/types.d.ts +5 -6
- package/lib/roles/types.d.ts.map +1 -1
- package/lib/roles/types.js.map +1 -1
- package/lib/run-budget.d.ts +4 -0
- package/lib/run-budget.d.ts.map +1 -0
- package/lib/run-budget.js +2 -0
- package/lib/run-budget.js.map +1 -0
- package/lib/runs.d.ts +9 -10
- package/lib/runs.d.ts.map +1 -1
- package/lib/secrets.d.ts +10 -21
- package/lib/secrets.d.ts.map +1 -1
- package/lib/store/agent-approval.d.ts +4 -5
- package/lib/store/agent-approval.d.ts.map +1 -1
- package/lib/store/agent-approval.js.map +1 -1
- package/lib/store/agent-run.d.ts +50 -52
- package/lib/store/agent-run.d.ts.map +1 -1
- package/lib/store/agent-run.js.map +1 -1
- package/lib/store/collections.d.ts +11 -12
- package/lib/store/collections.d.ts.map +1 -1
- package/lib/store/collections.js.map +1 -1
- package/lib/store/conversation-state.d.ts +14 -8
- package/lib/store/conversation-state.d.ts.map +1 -1
- package/lib/store/doc-analyzer.d.ts +7 -8
- package/lib/store/doc-analyzer.d.ts.map +1 -1
- package/lib/store/dsl-workflow.d.ts +10 -12
- package/lib/store/dsl-workflow.d.ts.map +1 -1
- package/lib/store/dsl-workflow.js.map +1 -1
- package/lib/store/duration.d.ts +12 -0
- package/lib/store/duration.d.ts.map +1 -0
- package/lib/store/duration.js +2 -0
- package/lib/store/duration.js.map +1 -0
- package/lib/store/grounded-extraction.d.ts +6 -7
- package/lib/store/grounded-extraction.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.js +143 -2
- package/lib/store/intake-policy-schema.generated.js.map +1 -1
- package/lib/store/process.d.ts +70 -69
- package/lib/store/process.d.ts.map +1 -1
- package/lib/store/process.js.map +1 -1
- package/lib/store/rendering.d.ts +6 -12
- package/lib/store/rendering.d.ts.map +1 -1
- package/lib/store/rendering.js +0 -5
- package/lib/store/rendering.js.map +1 -1
- package/lib/store/signals.d.ts +43 -0
- package/lib/store/signals.d.ts.map +1 -1
- package/lib/store/signals.js +6 -1
- package/lib/store/signals.js.map +1 -1
- package/lib/store/store.d.ts +92 -91
- package/lib/store/store.d.ts.map +1 -1
- package/lib/store/store.js.map +1 -1
- package/lib/store/task.d.ts +10 -14
- package/lib/store/task.d.ts.map +1 -1
- package/lib/store/workflow.d.ts +35 -41
- package/lib/store/workflow.d.ts.map +1 -1
- package/lib/store/workflow.js.map +1 -1
- package/lib/sts-token-types.d.ts +14 -19
- package/lib/sts-token-types.d.ts.map +1 -1
- package/lib/tools.d.ts +12 -13
- package/lib/tools.d.ts.map +1 -1
- package/lib/transient-tokens.d.ts +3 -3
- package/lib/transient-tokens.d.ts.map +1 -1
- package/lib/user.d.ts +16 -20
- package/lib/user.d.ts.map +1 -1
- package/lib/user.js.map +1 -1
- package/lib/vertesia-common.js +2 -2
- package/lib/vertesia-common.js.map +1 -1
- package/lib/views.d.ts +61 -64
- package/lib/views.d.ts.map +1 -1
- package/lib/views.js.map +1 -1
- package/lib/wire-types.generated.d.ts +11227 -0
- package/lib/wire-types.generated.d.ts.map +1 -0
- package/lib/wire-types.generated.js +4 -0
- package/lib/wire-types.generated.js.map +1 -0
- package/lib/workflow-analytics.d.ts +21 -19
- package/lib/workflow-analytics.d.ts.map +1 -1
- package/lib/workflow-analytics.js +2 -2
- package/lib/workflow-analytics.js.map +1 -1
- package/package.json +16 -11
- package/src/access-control-values.ts +2 -2
- package/src/access-control.ts +7 -15
- package/src/analytics.ts +6 -13
- package/src/api-contract/components.generated.json +2553 -1046
- package/src/api-contract/engine.ts +641 -0
- package/src/api-contract/index.ts +30 -661
- package/src/api-schemas/access-control.ts +18 -16
- package/src/api-schemas/account.ts +5 -12
- package/src/api-schemas/agent-budget.ts +60 -0
- package/src/api-schemas/agent-runs.ts +63 -27
- package/src/api-schemas/apikey.ts +11 -10
- package/src/api-schemas/app-lifecycle.ts +5 -0
- package/src/api-schemas/audit-trail.ts +175 -1
- package/src/api-schemas/content.ts +55 -0
- package/src/api-schemas/cost-analytics.ts +42 -2
- package/src/api-schemas/environment.ts +0 -1
- package/src/api-schemas/group.ts +2 -1
- package/src/api-schemas/index.ts +2 -0
- package/src/api-schemas/inference-profile.ts +151 -0
- package/src/api-schemas/interaction.ts +40 -1
- package/src/api-schemas/invites.ts +10 -9
- package/src/api-schemas/process.ts +78 -2
- package/src/api-schemas/project-configuration.ts +19 -1
- package/src/api-schemas/project.ts +19 -10
- package/src/api-schemas/quota.ts +7 -6
- package/src/api-schemas/registry.ts +164 -62
- package/src/api-schemas/run-budget.ts +23 -0
- package/src/api-schemas/secrets.ts +0 -84
- package/src/api-schemas/store.ts +12 -0
- package/src/api-schemas/user.ts +9 -8
- package/src/api-schemas/workflow-runs.ts +2 -0
- package/src/apikey.ts +30 -39
- package/src/apps.ts +116 -239
- package/src/audit-trail.ts +37 -35
- package/src/browser-credentials.ts +11 -23
- package/src/common.ts +17 -37
- package/src/content-query.ts +3 -4
- package/src/cost-analytics.ts +13 -35
- package/src/data-platform.ts +79 -168
- package/src/delegation.ts +4 -9
- package/src/email.ts +15 -29
- package/src/embeddings.ts +25 -51
- package/src/environment.ts +16 -33
- package/src/facets.ts +3 -4
- package/src/group.ts +6 -13
- package/src/index.ts +23 -0
- package/src/inference-profile.ts +20 -0
- package/src/integrations.ts +21 -41
- package/src/interaction.ts +68 -137
- package/src/meters.ts +4 -9
- package/src/oauth-server.ts +40 -81
- package/src/oauth.ts +9 -19
- package/src/payload.ts +11 -25
- package/src/pending-asks.ts +8 -22
- package/src/platform-event.ts +48 -122
- package/src/principal-context.ts +3 -3
- package/src/project.ts +43 -86
- package/src/prompt.ts +14 -31
- package/src/query.ts +10 -20
- package/src/rate-limiter.ts +7 -19
- package/src/refs.ts +2 -3
- package/src/roles/types.ts +5 -11
- package/src/run-budget.ts +4 -0
- package/src/runs.ts +9 -19
- package/src/secrets.ts +10 -42
- package/src/store/agent-approval.ts +4 -9
- package/src/store/agent-run.ts +53 -99
- package/src/store/collections.ts +11 -23
- package/src/store/conversation-state.ts +15 -15
- package/src/store/doc-analyzer.ts +7 -15
- package/src/store/dsl-workflow.ts +10 -23
- package/src/store/duration.ts +45 -0
- package/src/store/grounded-extraction.ts +6 -13
- package/src/store/intake-policy-schema.generated.ts +148 -2
- package/src/store/process.ts +70 -140
- package/src/store/rendering.ts +6 -19
- package/src/store/signals.ts +48 -0
- package/src/store/store.ts +92 -181
- package/src/store/task.ts +10 -24
- package/src/store/workflow.ts +35 -71
- package/src/sts-token-types.ts +14 -33
- package/src/tools.ts +12 -25
- package/src/transient-tokens.ts +3 -3
- package/src/user.ts +16 -40
- package/src/views.ts +61 -125
- package/src/wire-types.generated.ts +11888 -0
- package/src/workflow-analytics.ts +21 -19
- package/src/agent-resources.test.ts +0 -89
- package/src/api-contract/components.contract.test.ts +0 -99
- package/src/api-schemas/access-control.contract.test.ts +0 -209
- package/src/api-schemas/account-apikey.test.ts +0 -60
- package/src/api-schemas/account.contract.test.ts +0 -319
- package/src/api-schemas/adapter-edge-cases.test.ts +0 -499
- package/src/api-schemas/agent-communication.contract.test.ts +0 -90
- package/src/api-schemas/agent-history.contract.test.ts +0 -42
- package/src/api-schemas/agent-runs.contract.test.ts +0 -240
- package/src/api-schemas/api-discriminators.test.ts +0 -185
- package/src/api-schemas/apikey.contract.test.ts +0 -270
- package/src/api-schemas/app-delete-summary.contract.test.ts +0 -67
- package/src/api-schemas/bulk-operation.contract.test.ts +0 -39
- package/src/api-schemas/bundle-isolation.test.ts +0 -44
- package/src/api-schemas/commands.contract.test.ts +0 -51
- package/src/api-schemas/cost-analytics.contract.test.ts +0 -64
- package/src/api-schemas/dashboard.contract.test.ts +0 -138
- package/src/api-schemas/data-store.contract.test.ts +0 -97
- package/src/api-schemas/document-processing.contract.test.ts +0 -76
- package/src/api-schemas/emit-json-schema.test.ts +0 -36
- package/src/api-schemas/environment.contract.test.ts +0 -202
- package/src/api-schemas/group.contract.test.ts +0 -205
- package/src/api-schemas/indexing.contract.test.ts +0 -122
- package/src/api-schemas/interaction.contract.test.ts +0 -307
- package/src/api-schemas/invites.contract.test.ts +0 -268
- package/src/api-schemas/llumiverse.contract.test.ts +0 -179
- package/src/api-schemas/mcp-api-key.contract.test.ts +0 -125
- package/src/api-schemas/oauth.contract.test.ts +0 -181
- package/src/api-schemas/parameters.test.ts +0 -402
- package/src/api-schemas/process.contract.test.ts +0 -115
- package/src/api-schemas/project.contract.test.ts +0 -472
- package/src/api-schemas/pruning-limits.test.ts +0 -189
- package/src/api-schemas/quota.contract.test.ts +0 -147
- package/src/api-schemas/registry-groups.test.ts +0 -99
- package/src/api-schemas/response-pruning.test.ts +0 -260
- package/src/api-schemas/store.contract.test.ts +0 -378
- package/src/api-schemas/sts.contract.test.ts +0 -112
- package/src/api-schemas/user.contract.test.ts +0 -197
- package/src/api-schemas/views.contract.test.ts +0 -112
- package/src/api-schemas/zeno-response-contracts.test.ts +0 -380
- package/src/apps.test.ts +0 -89
- package/src/oauth-scopes.test.ts +0 -15
- package/src/roles.test.ts +0 -33
- package/src/schema-for-extraction.test.ts +0 -191
- package/src/store/agent-approval.test.ts +0 -18
- package/src/store/content-type-editing.test.ts +0 -17
- package/src/store/ms.d.ts +0 -42
- package/src/store/schedule.test.ts +0 -59
- package/src/store/workflow.test.ts +0 -640
- package/src/versions.test.ts +0 -13
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { AccountType, BillingMethod } from '../user.js';
|
|
4
|
-
import { findUnprunablePaths, type JsonObject, pruneToSchema, toOpenApiComponents } from './adapter.js';
|
|
5
|
-
import {
|
|
6
|
-
type ApiComponentType,
|
|
7
|
-
ApiSchemaComponents,
|
|
8
|
-
apiComponentRef,
|
|
9
|
-
pruneAndValidateApiResponse,
|
|
10
|
-
pruneApiResponse,
|
|
11
|
-
} from './registry.js';
|
|
12
|
-
|
|
13
|
-
function compile(name: string) {
|
|
14
|
-
const ajv = new Ajv2020({ strictSchema: false, allErrors: true });
|
|
15
|
-
ajv.addSchema({ $id: 'vertesia://openapi', components: { schemas: ApiSchemaComponents } });
|
|
16
|
-
return ajv.compile({ $ref: `vertesia://openapi${apiComponentRef(name as never)}` });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const ACCOUNT: ApiComponentType<'Account'> = {
|
|
20
|
-
id: 'acc_1',
|
|
21
|
-
name: 'Acme',
|
|
22
|
-
email_domains: ['acme.com'],
|
|
23
|
-
onboarding: { completed: true, completed_at: '2026-07-29T10:00:00.000Z' },
|
|
24
|
-
datacenter: 'gcp',
|
|
25
|
-
account_type: AccountType.customer,
|
|
26
|
-
billing: { method: BillingMethod.stripe, stripe_customer_id: 'cus_1' },
|
|
27
|
-
feature_flags: { new_editor: true, beta_agents: 'on' },
|
|
28
|
-
created_by: 'u1',
|
|
29
|
-
updated_by: 'u1',
|
|
30
|
-
created_at: '2026-07-29T10:00:00.000Z',
|
|
31
|
-
updated_at: '2026-07-29T10:00:00.000Z',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* An Account carrying extra runtime fields — what a Mongo document actually is. The cast models
|
|
36
|
-
* the gap the pruner exists to close: the value is typed as the component, but at runtime it has
|
|
37
|
-
* more than the component declares.
|
|
38
|
-
*/
|
|
39
|
-
const withExtras = (extra: Record<string, unknown>): ApiComponentType<'Account'> =>
|
|
40
|
-
({ ...ACCOUNT, ...extra }) as ApiComponentType<'Account'>;
|
|
41
|
-
|
|
42
|
-
describe('response pruning narrows payloads to the published contract', () => {
|
|
43
|
-
it('drops undeclared top-level fields', () => {
|
|
44
|
-
const pruned = pruneApiResponse(
|
|
45
|
-
'Account',
|
|
46
|
-
withExtras({ __v: 3, internal_notes: 'do not ship', stripe_secret: 'sk_live_leak' }),
|
|
47
|
-
);
|
|
48
|
-
expect(pruned).toEqual(ACCOUNT);
|
|
49
|
-
expect('stripe_secret' in pruned).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('drops undeclared fields nested inside a declared object', () => {
|
|
53
|
-
const pruned = pruneApiResponse(
|
|
54
|
-
'Account',
|
|
55
|
-
withExtras({
|
|
56
|
-
onboarding: { ...ACCOUNT.onboarding, internal_step: 7 },
|
|
57
|
-
billing: { ...ACCOUNT.billing, stripe_secret_key: 'sk_live_leak' },
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
60
|
-
expect(pruned.onboarding).toEqual(ACCOUNT.onboarding);
|
|
61
|
-
expect(pruned.billing).toEqual(ACCOUNT.billing);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('PRESERVES the contents of freeform maps', () => {
|
|
65
|
-
// The regression this design exists to avoid: AJV's removeAdditional:'all' strips keys
|
|
66
|
-
// from objects that declare additionalProperties too, which is how launch_workstream's
|
|
67
|
-
// `data` was emptied. `feature_flags` is `{additionalProperties:{}}` with no `properties`,
|
|
68
|
-
// so every operator flag would vanish under a blanket removal mode.
|
|
69
|
-
const pruned = pruneApiResponse('Account', ACCOUNT);
|
|
70
|
-
expect(pruned.feature_flags).toEqual({ new_editor: true, beta_agents: 'on' });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('leaves a conforming payload byte-identical', () => {
|
|
74
|
-
expect(pruneApiResponse('Account', ACCOUNT)).toEqual(ACCOUNT);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('never mutates the payload it is given', () => {
|
|
78
|
-
const input = withExtras({ rogue: 1 });
|
|
79
|
-
const pristine = structuredClone(input);
|
|
80
|
-
pruneApiResponse('Account', input);
|
|
81
|
-
expect(input).toEqual(pristine);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('produces output that validates against the published schema', () => {
|
|
85
|
-
const validate = compile('Account');
|
|
86
|
-
const pruned = pruneApiResponse('Account', withExtras({ leaked: 'x' }));
|
|
87
|
-
expect(validate(pruned)).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('does not throw on a payload that is missing required fields', () => {
|
|
91
|
-
// Pruning is not validation. A malformed document still reaches the client rather than
|
|
92
|
-
// becoming a 500 — surfacing drift is the validator's job, not the pruner's.
|
|
93
|
-
const malformed = { id: 'acc_1', junk: true } as unknown as ApiComponentType<'Account'>;
|
|
94
|
-
expect(() => pruneApiResponse('Account', malformed)).not.toThrow();
|
|
95
|
-
expect(pruneApiResponse('Account', malformed)).toEqual({ id: 'acc_1' });
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('pruneAndValidateApiResponse is the sound entry point for untyped payloads', () => {
|
|
100
|
-
it('returns the component type only on the valid branch', () => {
|
|
101
|
-
const result = pruneAndValidateApiResponse('Account', withExtras({ leaked: 'x' }));
|
|
102
|
-
expect(result.valid).toBe(true);
|
|
103
|
-
if (result.valid) {
|
|
104
|
-
// Reachable as the component type only after validation succeeded.
|
|
105
|
-
const id: string = result.data.id;
|
|
106
|
-
expect(id).toBe('acc_1');
|
|
107
|
-
expect('leaked' in result.data).toBe(false);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('reports errors instead of typing an incomplete payload as complete', () => {
|
|
112
|
-
// The unsoundness this replaces: narrowing `{id}` and handing it back as a full Account.
|
|
113
|
-
const result = pruneAndValidateApiResponse('Account', { id: 'acc_1', junk: true });
|
|
114
|
-
expect(result.valid).toBe(false);
|
|
115
|
-
if (!result.valid) {
|
|
116
|
-
expect(result.errors.join(' ')).toContain("must have required property 'name'");
|
|
117
|
-
// The pruned value is still available, so the caller chooses ship-and-log or fail.
|
|
118
|
-
expect(result.data).toEqual({ id: 'acc_1' });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('flags a legacy account missing created_by/updated_by', () => {
|
|
123
|
-
// Real migration hazard: the Mongo schema declares these without `required`, and the
|
|
124
|
-
// BackfillAccountCreatedBy migration skips ownerless accounts by design.
|
|
125
|
-
const { created_by: _c, updated_by: _u, ...legacy } = ACCOUNT;
|
|
126
|
-
const result = pruneAndValidateApiResponse('Account', legacy);
|
|
127
|
-
expect(result.valid).toBe(false);
|
|
128
|
-
if (!result.valid) {
|
|
129
|
-
expect(result.errors.join(' ')).toContain('created_by');
|
|
130
|
-
expect(result.errors.join(' ')).toContain('updated_by');
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('response pruning through structural schema features', () => {
|
|
136
|
-
it('follows the discriminator to the matching union branch', () => {
|
|
137
|
-
const enabled = pruneApiResponse('StripeBillingStatusResponse', {
|
|
138
|
-
status: 'enabled',
|
|
139
|
-
billing_method: 'stripe',
|
|
140
|
-
portal_url: 'https://billing',
|
|
141
|
-
internal_session_id: 'leak',
|
|
142
|
-
} as never);
|
|
143
|
-
expect(enabled).toEqual({ status: 'enabled', billing_method: 'stripe', portal_url: 'https://billing' });
|
|
144
|
-
|
|
145
|
-
const disabled = pruneApiResponse('StripeBillingStatusResponse', {
|
|
146
|
-
status: 'disabled',
|
|
147
|
-
billing_method: null,
|
|
148
|
-
reason: 'No billing method',
|
|
149
|
-
internal_trace: 'leak',
|
|
150
|
-
} as never);
|
|
151
|
-
expect(disabled).toEqual({ status: 'disabled', billing_method: null, reason: 'No billing method' });
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('prunes inside arrays', () => {
|
|
155
|
-
const components = toOpenApiComponents({
|
|
156
|
-
Page: {
|
|
157
|
-
type: 'object',
|
|
158
|
-
properties: {
|
|
159
|
-
items: {
|
|
160
|
-
type: 'array',
|
|
161
|
-
items: { $id: 'Row', type: 'object', properties: { id: { type: 'string' } } },
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
const pruned = pruneToSchema(
|
|
167
|
-
{
|
|
168
|
-
items: [
|
|
169
|
-
{ id: 'a', secret: 1 },
|
|
170
|
-
{ id: 'b', secret: 2 },
|
|
171
|
-
],
|
|
172
|
-
extra: true,
|
|
173
|
-
},
|
|
174
|
-
{ $ref: '#/components/schemas/Page' },
|
|
175
|
-
components,
|
|
176
|
-
) as JsonObject;
|
|
177
|
-
expect(pruned).toEqual({ items: [{ id: 'a' }, { id: 'b' }] });
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('leaves ambiguous schemas untouched rather than guessing', () => {
|
|
181
|
-
// An undiscriminated union and an allOf could each drop fields that belong to a branch we
|
|
182
|
-
// cannot identify, so the payload passes through intact.
|
|
183
|
-
const components = toOpenApiComponents({
|
|
184
|
-
Loose: { anyOf: [{ type: 'object', properties: { a: { type: 'string' } } }, { type: 'object' }] },
|
|
185
|
-
Merged: { allOf: [{ type: 'object', properties: { a: { type: 'string' } } }] },
|
|
186
|
-
});
|
|
187
|
-
const payload = { a: 'x', b: 'keep' };
|
|
188
|
-
expect(pruneToSchema(payload, { $ref: '#/components/schemas/Loose' }, components)).toEqual(payload);
|
|
189
|
-
expect(pruneToSchema(payload, { $ref: '#/components/schemas/Merged' }, components)).toEqual(payload);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('keeps extras where a component explicitly declares itself extensible', () => {
|
|
193
|
-
const components = toOpenApiComponents({
|
|
194
|
-
Open: { type: 'object', properties: { a: { type: 'string' } }, additionalProperties: true },
|
|
195
|
-
});
|
|
196
|
-
expect(pruneToSchema({ a: 'x', b: 'keep' }, { $ref: '#/components/schemas/Open' }, components)).toEqual({
|
|
197
|
-
a: 'x',
|
|
198
|
-
b: 'keep',
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('keeps properties documented on a $ref sibling', () => {
|
|
203
|
-
// 2020-12 applies $ref siblings as additional constraints, so `extra` is declared here
|
|
204
|
-
// and must survive pruning rather than being dropped as unknown.
|
|
205
|
-
const components = toOpenApiComponents({
|
|
206
|
-
Wrapper: {
|
|
207
|
-
type: 'object',
|
|
208
|
-
properties: {
|
|
209
|
-
inner: {
|
|
210
|
-
$ref: '#/$defs/Base',
|
|
211
|
-
properties: { extra: { type: 'string' } },
|
|
212
|
-
required: ['extra'],
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
$defs: { Base: { type: 'object', properties: { a: { type: 'string' } }, required: ['a'] } },
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
const pruned = pruneToSchema(
|
|
219
|
-
{ inner: { a: 'x', extra: 'documented', undeclared: 'drop' } },
|
|
220
|
-
{ $ref: '#/components/schemas/Wrapper' },
|
|
221
|
-
components,
|
|
222
|
-
);
|
|
223
|
-
expect(pruned).toEqual({ inner: { a: 'x', extra: 'documented' } });
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('follows a chain of aliased references to the terminal schema', () => {
|
|
227
|
-
// A component that is itself a reference is an alias. Resolving only one hop would leave a
|
|
228
|
-
// node whose `properties` lists just `extra`, so pruning would read that as the entire
|
|
229
|
-
// shape and drop `a` — which Base requires, making the pruned response fail validation.
|
|
230
|
-
const components = toOpenApiComponents({
|
|
231
|
-
Alias: {
|
|
232
|
-
$ref: '#/components/schemas/Base',
|
|
233
|
-
properties: { extra: { type: 'string' } },
|
|
234
|
-
required: ['extra'],
|
|
235
|
-
},
|
|
236
|
-
Base: { type: 'object', properties: { a: { type: 'string' } }, required: ['a'] },
|
|
237
|
-
});
|
|
238
|
-
const ref = { $ref: '#/components/schemas/Alias' };
|
|
239
|
-
|
|
240
|
-
const pruned = pruneToSchema({ a: 'x', extra: 'documented', undeclared: 'drop' }, ref, components);
|
|
241
|
-
expect(pruned).toEqual({ a: 'x', extra: 'documented' });
|
|
242
|
-
// The detector recurses through references, so it must reach the same terminal schema —
|
|
243
|
-
// a divergence here means it certifies a component the pruner then mangles.
|
|
244
|
-
expect(findUnprunablePaths(ref, components)).toEqual([]);
|
|
245
|
-
|
|
246
|
-
const ajv = new Ajv2020({ strictSchema: false, allErrors: true });
|
|
247
|
-
ajv.addSchema({ $id: 'vertesia://chain', components: { schemas: components } });
|
|
248
|
-
expect(ajv.validate({ $ref: 'vertesia://chain#/components/schemas/Alias' }, pruned)).toBe(true);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('strips undeclared fields on a component closed with additionalProperties:false', () => {
|
|
252
|
-
const components = toOpenApiComponents(
|
|
253
|
-
{ Closed: { type: 'object', properties: { a: { type: 'string' } } } },
|
|
254
|
-
{ strictComponents: new Set(['Closed']) },
|
|
255
|
-
);
|
|
256
|
-
expect(pruneToSchema({ a: 'x', b: 'drop' }, { $ref: '#/components/schemas/Closed' }, components)).toEqual({
|
|
257
|
-
a: 'x',
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
});
|
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import { Ajv } from 'ajv';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { ContentTypeIntakePolicySchema as GeneratedIntakePolicySchema } from '../store/intake-policy-schema.generated.js';
|
|
4
|
-
import type { ContentObjectTypeRef, ContentTypeIntakePolicy, IntakePageRanges } from '../store/store.js';
|
|
5
|
-
import type { JsonObject } from './adapter.js';
|
|
6
|
-
import { ApiSchemaComponents, bundleCanonicalComponent, validateApiResponse } from './registry.js';
|
|
7
|
-
import { ContentTypeIntakePolicySchema } from './store.js';
|
|
8
|
-
|
|
9
|
-
/** Exact type identity — `extends` in both directions is too weak (any/unknown slip through). */
|
|
10
|
-
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
11
|
-
function assertType<T extends true>(_ok: T): void {}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The intake policy is now stated once, in Zod, and reaches its consumers three ways: as the
|
|
15
|
-
* canonical component, as the checked-in JSON artifact consumers compile, and as the TypeScript
|
|
16
|
-
* type. This file keeps those package-owned contracts aligned. Downstream consumers test their
|
|
17
|
-
* published API documents and generated-client fixtures separately.
|
|
18
|
-
*
|
|
19
|
-
* It also pins the behaviour changes the conversion makes, because the schema it replaced was
|
|
20
|
-
* hand-written and disagreed with the contract in both directions. Each is asserted rather than
|
|
21
|
-
* described: the whole point of the migration is that a disagreement fails a build instead of
|
|
22
|
-
* shipping.
|
|
23
|
-
*/
|
|
24
|
-
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
25
|
-
const validate = ajv.compile<ContentTypeIntakePolicy>(GeneratedIntakePolicySchema);
|
|
26
|
-
|
|
27
|
-
function errors(): string {
|
|
28
|
-
return ajv.errorsText(validate.errors, { separator: '\n' });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('generated artifact and canonical component', () => {
|
|
32
|
-
it('is the exact bundle the registry emits, so the artifact is compiled output', () => {
|
|
33
|
-
// Deep equality, not a spot check: the file is generated by `pnpm run gen:schemas` from this
|
|
34
|
-
// same function, so any difference means someone hand-edited it or forgot to regenerate.
|
|
35
|
-
expect(GeneratedIntakePolicySchema).toEqual(bundleCanonicalComponent('ContentTypeIntakePolicy'));
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('registers the component the alias claims, under the id the alias name requires', () => {
|
|
39
|
-
expect(Object.keys(ApiSchemaComponents)).toContain('ContentTypeIntakePolicy');
|
|
40
|
-
assertType<Equals<ContentTypeIntakePolicy, (typeof ContentTypeIntakePolicySchema)['_zod']['output']>>(true);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('content object response classification', () => {
|
|
45
|
-
it('accepts the current sensitivity and compartments fields', () => {
|
|
46
|
-
const result = validateApiResponse('ContentObjectApiResponse', {
|
|
47
|
-
id: 'content-object-id',
|
|
48
|
-
name: 'Classified document',
|
|
49
|
-
updated_by: 'user-id',
|
|
50
|
-
created_by: 'user-id',
|
|
51
|
-
created_at: '2026-08-06T09:00:00.000Z',
|
|
52
|
-
updated_at: '2026-08-06T09:00:00.000Z',
|
|
53
|
-
location: '/',
|
|
54
|
-
status: 'completed',
|
|
55
|
-
properties: {},
|
|
56
|
-
revision: { root: 'content-object-id', head: true },
|
|
57
|
-
sensitivity: 3,
|
|
58
|
-
compartments: ['legal'],
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(result).toMatchObject({ valid: true });
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Bodies that probe where the two validators could disagree.
|
|
67
|
-
*
|
|
68
|
-
* The gate above compares the artifact to the component, and both are produced by the SAME adapter
|
|
69
|
-
* from the SAME Zod schema — so any transform applied on the way out is invisible to it. That blind
|
|
70
|
-
* spot shipped once: an adapter pass stripped the bounds `z.int()` attaches for the JavaScript
|
|
71
|
-
* safe-integer range, on the reasoning that no JSON value could fall outside them. `1e20` does, and
|
|
72
|
-
* the Zod schema rejected it while the generated schema accepted it. Nothing in a
|
|
73
|
-
* schema-versus-schema comparison can see that; only running both validators over the same value can.
|
|
74
|
-
*/
|
|
75
|
-
const PROBES: unknown[] = [
|
|
76
|
-
// Numeric range, including the values that made the sentinel-stripping bug observable.
|
|
77
|
-
{},
|
|
78
|
-
{ extraction: { max_pages: 1 } },
|
|
79
|
-
{ extraction: { max_pages: 0 } },
|
|
80
|
-
{ extraction: { max_pages: 2.5 } },
|
|
81
|
-
{ extraction: { max_pages: 1e20 } },
|
|
82
|
-
{ extraction: { max_pages: 2147483647 } },
|
|
83
|
-
{ extraction: { max_pages: 2147483648 } },
|
|
84
|
-
{ extraction: { max_pages: Number.MAX_SAFE_INTEGER + 1 } },
|
|
85
|
-
{ extraction: { max_pages: -1 } },
|
|
86
|
-
{ locate: { instructions: 'x', min_pages: 0 } },
|
|
87
|
-
{ locate: { instructions: 'x', min_pages: -1 } },
|
|
88
|
-
{ locate: { instructions: 'x', min_pages: 1e20 } },
|
|
89
|
-
{ locate: { detail: 8 } },
|
|
90
|
-
{ text_conversion: { render_dpi: 72 } },
|
|
91
|
-
{ text_conversion: { render_dpi: 71 } },
|
|
92
|
-
{ text_conversion: { render_dpi: 1e20 } },
|
|
93
|
-
{ extraction: { verification: { threshold: 0 } } },
|
|
94
|
-
{ extraction: { verification: { threshold: 1 } } },
|
|
95
|
-
{ extraction: { verification: { threshold: 1.000001 } } },
|
|
96
|
-
{ extraction: { verification: { max_retries: 1e20 } } },
|
|
97
|
-
{ extraction: { vision: { max_image_tokens: 1e20, max_payload_mb: 1e20 } } },
|
|
98
|
-
{ extraction: { grounding: { window_pages: 1e20, grid_cell_pt: 1e20 } } },
|
|
99
|
-
{ extraction: { grounding: { hardness_threshold: 1e20 } } },
|
|
100
|
-
// Page ranges: length, element type, and element magnitude.
|
|
101
|
-
{ extraction: { page_ranges: [[1, 2]] } },
|
|
102
|
-
{ extraction: { page_ranges: [[-1, -1]] } },
|
|
103
|
-
{ extraction: { page_ranges: [[1]] } },
|
|
104
|
-
{ extraction: { page_ranges: [[1, 2, 3]] } },
|
|
105
|
-
{ extraction: { page_ranges: [[1.5, 2]] } },
|
|
106
|
-
{ extraction: { page_ranges: [[1e20, 2]] } },
|
|
107
|
-
// Nullability, unknown keys, and the enums.
|
|
108
|
-
{ generate_toc: null },
|
|
109
|
-
{ extraction: null },
|
|
110
|
-
{ extraction: { max_pages: null } },
|
|
111
|
-
{ grounded_extraction: { enabled: true } },
|
|
112
|
-
{ locate: { instructions: 'x', dpi: 300 } },
|
|
113
|
-
{ extraction: { scope: 'first-two' } },
|
|
114
|
-
{ extraction: { vision: { default_detail: 'ultra' } } },
|
|
115
|
-
{ default_view: 'pdf' },
|
|
116
|
-
{ mode: 'agentic' },
|
|
117
|
-
// The execution config: closedness, the option union, the two enums.
|
|
118
|
-
{ extraction: { config: { model: 'm' } } },
|
|
119
|
-
{ extraction: { config: { temperature: 0.5 } } },
|
|
120
|
-
{ extraction: { config: { http_timeout: { bodyTimeout: 1000 } } } },
|
|
121
|
-
{ extraction: { config: { http_timeout: { socketTimeout: 1000 } } } },
|
|
122
|
-
{ extraction: { config: { model_options: { _option_id: 'openai-text', temperature: 0.5 } } } },
|
|
123
|
-
{ extraction: { config: { model_options: { temperature: 0.5 } } } },
|
|
124
|
-
{ extraction: { config: { model_options: { _option_id: 'not-a-driver' } } } },
|
|
125
|
-
{ extraction: { config: { run_data: 'STANDARD' } } },
|
|
126
|
-
{ extraction: { config: { run_data: 'WHATEVER' } } },
|
|
127
|
-
{ extraction: { config: { configMode: 'RUN_CONFIG_ONLY' } } },
|
|
128
|
-
// Wrong types where a shape is expected.
|
|
129
|
-
{ extraction: 'nope' },
|
|
130
|
-
{ embeddings: { text: 'yes' } },
|
|
131
|
-
{ identification: { examples: 'not-a-list' } },
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
describe('gate — the artifact accepts exactly what the Zod schema accepts', () => {
|
|
135
|
-
it.each(PROBES.map((probe) => [JSON.stringify(probe), probe] as const))('agrees on %s', (_label, probe) => {
|
|
136
|
-
// The point of the migration is that ONE schema is enforced. Two validators built from it
|
|
137
|
-
// that disagree on a value means one of the consumers is enforcing something the source
|
|
138
|
-
// never said, which is the drift this whole exercise removes.
|
|
139
|
-
expect(validate(probe)).toBe(ContentTypeIntakePolicySchema.safeParse(probe).success);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('the TypeScript type says what the schema says', () => {
|
|
144
|
-
it('widens page ranges to number[][], which is what the component has always described', () => {
|
|
145
|
-
// `[number, number][]` is what the interface used to claim. The published component is
|
|
146
|
-
// `items: {type: integer}` with `minItems`/`maxItems` — a two-element ARRAY, not a tuple —
|
|
147
|
-
// because a Zod tuple emits `prefixItems`, which generated Java and Go clients read
|
|
148
|
-
// differently. The wire shape is unchanged; the SDK type is corrected to match it.
|
|
149
|
-
assertType<Equals<IntakePageRanges, number[][]>>(true);
|
|
150
|
-
expect(validate({ extraction: { page_ranges: [[1, 2]] } }), errors()).toBe(true);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('keeps the two type refs’ default_view in step with the policy', () => {
|
|
154
|
-
// Those refs spell the union out rather than writing `ContentTypeIntakePolicy['default_view']`,
|
|
155
|
-
// because indexing into a canonical alias makes the generator DROP the property instead of
|
|
156
|
-
// failing. This is what stops the restatement from drifting.
|
|
157
|
-
assertType<
|
|
158
|
-
Equals<
|
|
159
|
-
NonNullable<ContentObjectTypeRef['default_view']>,
|
|
160
|
-
NonNullable<ContentTypeIntakePolicy['default_view']>
|
|
161
|
-
>
|
|
162
|
-
>(true);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('parity with the hand-written AJV schema it replaces', () => {
|
|
167
|
-
it('still accepts a full policy across every block', () => {
|
|
168
|
-
const policy: ContentTypeIntakePolicy = {
|
|
169
|
-
mode: 'programmatic',
|
|
170
|
-
identification: { guidance: 'Investor reports' },
|
|
171
|
-
locate: { instructions: 'allocation tables', detail: 8, min_pages: 10 },
|
|
172
|
-
text_conversion: { method: 'llm', scope: 'located', render_dpi: 150 },
|
|
173
|
-
extraction: {
|
|
174
|
-
source: 'vision',
|
|
175
|
-
page_ranges: [
|
|
176
|
-
[1, 2],
|
|
177
|
-
[-1, -1],
|
|
178
|
-
],
|
|
179
|
-
max_pages: 6,
|
|
180
|
-
vision: { default_detail: 'high', max_image_tokens: 9000, max_payload_mb: 12 },
|
|
181
|
-
verification: { threshold: 0.7, max_retries: 2 },
|
|
182
|
-
grounding: {
|
|
183
|
-
enabled: true,
|
|
184
|
-
max_pages: 12,
|
|
185
|
-
hardness_threshold: 0.5,
|
|
186
|
-
config: { model: 'claude-sonnet-4-5' },
|
|
187
|
-
review: { threshold: 0.7, coverage_threshold: 0.2 },
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
embeddings: { text: true, properties: true },
|
|
191
|
-
default_view: 'pdf',
|
|
192
|
-
};
|
|
193
|
-
expect(validate(policy), errors()).toBe(true);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('keeps every numeric bound the hand-written schema enforced', () => {
|
|
197
|
-
// These were NOT in the published component — the interface carried no `@minimum` tags — so
|
|
198
|
-
// converting to Zod dropped all eighteen of them until this test caught it. They are restored
|
|
199
|
-
// in the schema, which means the document now publishes constraints the server has enforced
|
|
200
|
-
// all along.
|
|
201
|
-
expect(validate({ locate: { instructions: 'x', min_pages: -1 } })).toBe(false);
|
|
202
|
-
expect(validate({ text_conversion: { render_dpi: 71 } })).toBe(false);
|
|
203
|
-
expect(validate({ extraction: { max_pages: 0 } })).toBe(false);
|
|
204
|
-
expect(validate({ extraction: { verification: { threshold: 1.2 } } })).toBe(false);
|
|
205
|
-
expect(validate({ extraction: { verification: { max_retries: -1 } } })).toBe(false);
|
|
206
|
-
expect(validate({ extraction: { vision: { max_image_tokens: 0 } } })).toBe(false);
|
|
207
|
-
expect(validate({ extraction: { vision: { max_payload_mb: 0 } } })).toBe(false);
|
|
208
|
-
expect(validate({ extraction: { vision: { max_pages_per_call: 0 } } })).toBe(false);
|
|
209
|
-
expect(validate({ extraction: { grounding: { max_pages: 0 } } })).toBe(false);
|
|
210
|
-
expect(validate({ extraction: { grounding: { window_pages: 0 } } })).toBe(false);
|
|
211
|
-
expect(validate({ extraction: { grounding: { grid_cell_pt: 0 } } })).toBe(false);
|
|
212
|
-
expect(validate({ extraction: { grounding: { hardness_threshold: -0.1 } } })).toBe(false);
|
|
213
|
-
expect(validate({ extraction: { grounding: { min_citation_density: 1.1 } } })).toBe(false);
|
|
214
|
-
expect(validate({ extraction: { grounding: { review: { threshold: 1.1 } } } })).toBe(false);
|
|
215
|
-
expect(validate({ extraction: { grounding: { review: { coverage_threshold: -0.1 } } } })).toBe(false);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('keeps the page and DPI counters integral', () => {
|
|
219
|
-
// `type: 'integer'`, which the published component also never stated. A fractional page cap
|
|
220
|
-
// was rejected before this conversion and is rejected after it.
|
|
221
|
-
expect(validate({ extraction: { max_pages: 2.5 } })).toBe(false);
|
|
222
|
-
expect(validate({ text_conversion: { render_dpi: 150.5 } })).toBe(false);
|
|
223
|
-
expect(validate({ extraction: { page_ranges: [[1.5, 2]] } })).toBe(false);
|
|
224
|
-
expect(validate({ extraction: { page_ranges: [[1]] } })).toBe(false);
|
|
225
|
-
expect(validate({ locate: { instructions: 'x', min_pages: 8.5 } })).toBe(false);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('keeps rejecting unknown keys and the obsolete root block', () => {
|
|
229
|
-
expect(validate({ grounded_extraction: { enabled: true } })).toBe(false);
|
|
230
|
-
expect(validate({ locate: { instructions: 'x', dpi: 300 } })).toBe(false);
|
|
231
|
-
expect(validate({ extraction: { vision: { default_detail: 'ultra' } } })).toBe(false);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
describe('runtime tightenings — recorded as operation 25 of the 1.5 runbook', () => {
|
|
236
|
-
it('rejects an explicit null where the hand-written schema accepted one', () => {
|
|
237
|
-
// The replaced schema carried `nullable: true` on 112 subschemas and AJV honours that keyword
|
|
238
|
-
// for OpenAPI compatibility, so 101 of them accepted `null` — which neither the TypeScript
|
|
239
|
-
// type nor the published component has ever allowed. The fields are optional, not nullable.
|
|
240
|
-
expect(validate({ generate_toc: null })).toBe(false);
|
|
241
|
-
expect(validate({ rendering_template: null })).toBe(false);
|
|
242
|
-
expect(validate({ extraction: { max_pages: null } })).toBe(false);
|
|
243
|
-
expect(validate({ extraction: null })).toBe(false);
|
|
244
|
-
// Omission is still how you say "inherit", which is what every one of those fields meant.
|
|
245
|
-
expect(validate({}), errors()).toBe(true);
|
|
246
|
-
expect(validate({ extraction: {} }), errors()).toBe(true);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('closes the execution config, which the published component already described closed', () => {
|
|
250
|
-
// `config` was `additionalProperties: true` in the hand-written schema while
|
|
251
|
-
// `InteractionExecutionConfiguration` has always been published closed. A stored policy
|
|
252
|
-
// carrying an undeclared key there is now rejected at write time.
|
|
253
|
-
expect(ApiSchemaComponents.InteractionExecutionConfiguration.additionalProperties).toBe(false);
|
|
254
|
-
expect(validate({ extraction: { config: { model: 'claude-sonnet-4-5' } } }), errors()).toBe(true);
|
|
255
|
-
expect(
|
|
256
|
-
validate({ extraction: { config: { prompt_cache_mode: 'required', prompt_cache_ttl_seconds: 3600 } } }),
|
|
257
|
-
errors(),
|
|
258
|
-
).toBe(true);
|
|
259
|
-
expect(validate({ extraction: { config: { prompt_cache_ttl_seconds: 59 } } })).toBe(false);
|
|
260
|
-
expect(validate({ extraction: { config: { temperature: 0.5 } } })).toBe(false);
|
|
261
|
-
expect(validate({ extraction: { config: { http_timeout: { socketTimeout: 1000 } } } })).toBe(false);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('validates model_options against the union instead of accepting any object', () => {
|
|
265
|
-
// Same class: `model_options` was `{type: object, additionalProperties: true}` in the
|
|
266
|
-
// hand-written schema and `$ref: ModelOptions` in the published component. The family hint
|
|
267
|
-
// is optional, but known field types and supplied IDs remain validated.
|
|
268
|
-
expect(ApiSchemaComponents.ContentTypeIntakePolicy).toBeDefined();
|
|
269
|
-
expect(
|
|
270
|
-
validate({
|
|
271
|
-
extraction: { config: { model_options: { _option_id: 'openai-text', temperature: 0.5 } } },
|
|
272
|
-
}),
|
|
273
|
-
errors(),
|
|
274
|
-
).toBe(true);
|
|
275
|
-
expect(validate({ extraction: { config: { model_options: { temperature: 0.5 } } } })).toBe(true);
|
|
276
|
-
expect(validate({ extraction: { config: { model_options: { _option_id: 'not-a-driver' } } } })).toBe(false);
|
|
277
|
-
expect(validate({ extraction: { config: { model_options: {} } } })).toBe(true);
|
|
278
|
-
expect(validate({ extraction: { config: { model_options: { temperature: 'hot' } } } })).toBe(false);
|
|
279
|
-
expect(validate({ extraction: { config: { model_options: { unknown_option: true } } } })).toBe(false);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('constrains the two config enums that were unconstrained strings', () => {
|
|
283
|
-
expect(validate({ extraction: { config: { run_data: 'STANDARD' } } }), errors()).toBe(true);
|
|
284
|
-
expect(validate({ extraction: { config: { run_data: 'WHATEVER' } } })).toBe(false);
|
|
285
|
-
expect(validate({ extraction: { config: { configMode: 'RUN_CONFIG_ONLY' } } }), errors()).toBe(true);
|
|
286
|
-
expect(validate({ extraction: { config: { configMode: 'WHATEVER' } } })).toBe(false);
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
describe('the five content-type shapes are composed, not repeated', () => {
|
|
291
|
-
const properties = (name: string) =>
|
|
292
|
-
Object.keys((ApiSchemaComponents[name] as { properties: Record<string, unknown> }).properties);
|
|
293
|
-
const property = (name: string, key: string) =>
|
|
294
|
-
(ApiSchemaComponents[name] as { properties: Record<string, JsonObject> }).properties[key];
|
|
295
|
-
|
|
296
|
-
it('gives every shape the same definition for a shared field', () => {
|
|
297
|
-
// The drift this guards: five hand-transcribed copies of `table_layout` could disagree about
|
|
298
|
-
// its item type or its description and nothing would notice. They come from one dictionary now.
|
|
299
|
-
const shapes = [
|
|
300
|
-
'ContentObjectTypeItem',
|
|
301
|
-
'ContentObjectType',
|
|
302
|
-
'ContentObjectTypeCatalogEntry',
|
|
303
|
-
'InCodeTypeDefinition',
|
|
304
|
-
'CreateContentObjectTypePayload',
|
|
305
|
-
];
|
|
306
|
-
for (const field of ['table_layout', 'object_schema', 'strict_mode', 'intake', 'editing', 'status']) {
|
|
307
|
-
const rendered = shapes.map((shape) => JSON.stringify(property(shape, field)));
|
|
308
|
-
expect(new Set(rendered).size, `${field} differs across the content-type shapes`).toBe(1);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('keeps the published property order, which composition must not reshuffle', () => {
|
|
313
|
-
// Order is contract: it decides the order of the generated clients' constructor arguments and
|
|
314
|
-
// model fields. The five orders differ because they were derived from five different types.
|
|
315
|
-
expect(properties('ContentObjectTypeItem')).toEqual(properties('ContentObjectType'));
|
|
316
|
-
expect(properties('ContentObjectTypeCatalogEntry').slice(0, 12)).toEqual(properties('InCodeTypeDefinition'));
|
|
317
|
-
expect(properties('CreateContentObjectTypePayload')).toEqual([
|
|
318
|
-
'status',
|
|
319
|
-
'is_chunkable',
|
|
320
|
-
'intake',
|
|
321
|
-
'editing',
|
|
322
|
-
'table_layout',
|
|
323
|
-
'object_schema',
|
|
324
|
-
'strict_mode',
|
|
325
|
-
'name',
|
|
326
|
-
'description',
|
|
327
|
-
'tags',
|
|
328
|
-
]);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('keeps the audit fields required on a stored type and optional in the catalog', () => {
|
|
332
|
-
// Not a detail: a catalog entry may be an in-code type contributed by a plugin, which nobody
|
|
333
|
-
// created and nobody has modified. Requiring the four there would reject a legitimate entry.
|
|
334
|
-
const audit = ['updated_by', 'created_by', 'created_at', 'updated_at'];
|
|
335
|
-
const required = (name: string) => (ApiSchemaComponents[name] as { required?: string[] }).required ?? [];
|
|
336
|
-
|
|
337
|
-
expect(required('ContentObjectTypeItem')).toEqual(expect.arrayContaining(audit));
|
|
338
|
-
expect(required('ContentObjectType')).toEqual(expect.arrayContaining(audit));
|
|
339
|
-
for (const field of audit) {
|
|
340
|
-
expect(required('ContentObjectTypeCatalogEntry')).not.toContain(field);
|
|
341
|
-
expect(property('ContentObjectTypeCatalogEntry', field)).toEqual({ type: 'string' });
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it('accepts a catalog entry for an in-code type that has no audit trail', () => {
|
|
346
|
-
expect(
|
|
347
|
-
validateApiResponse('ContentObjectTypeCatalogEntry', {
|
|
348
|
-
id: 'sys:Invoice',
|
|
349
|
-
name: 'Invoice',
|
|
350
|
-
is_chunkable: false,
|
|
351
|
-
}).valid,
|
|
352
|
-
).toBe(true);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('publishes `title` on the app-contribution shapes and withholds it from the stored ones', () => {
|
|
356
|
-
// An app package contributes a display title alongside the app-local `name`, exactly as
|
|
357
|
-
// `InCodeViewDefinition` and `InCodeProcessDefinition` do. Only the two shapes that carry a
|
|
358
|
-
// contribution get it; a type someone stored through the API has no `title` to publish.
|
|
359
|
-
for (const shape of ['InCodeTypeDefinition', 'ContentObjectTypeCatalogEntry']) {
|
|
360
|
-
expect(property(shape, 'title'), `${shape} should publish title`).toBeDefined();
|
|
361
|
-
}
|
|
362
|
-
for (const shape of ['ContentObjectTypeItem', 'ContentObjectType', 'CreateContentObjectTypePayload']) {
|
|
363
|
-
expect(property(shape, 'title'), `${shape} should not publish title`).toBeUndefined();
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('accepts an app-contributed type that carries a title', () => {
|
|
368
|
-
// The regression: deployed app packages send `title`, the shape was strict and did not
|
|
369
|
-
// declare it, so every `GET /projects/:projectId/app-types` failed its own response contract.
|
|
370
|
-
expect(
|
|
371
|
-
validateApiResponse('InCodeTypeDefinition', {
|
|
372
|
-
id: 'invoice',
|
|
373
|
-
name: 'invoice',
|
|
374
|
-
title: 'Invoice',
|
|
375
|
-
}).valid,
|
|
376
|
-
).toBe(true);
|
|
377
|
-
});
|
|
378
|
-
});
|