Closed peterlundberg closed 1 week ago
@peterlundberg Hi,
The AOT options for TypeBox are currently limited to Check functionality only. For errors, these are obtained via calls to Value.Errors()
which uses dynamic property checking which are not compiled. The Errors() function is shared between TypeCompiler and Value.* operations.
The recommendation for Content Security restricted environments is to simply use Value.*
functions instead of the compiler. These functions are simpler to use, and do not require eval
to work. They do however come at a performance cost, but should be more than adequate for most browser level workloads.
If you do need high performance checking browser side while operating in a eval restricted context, you can try the following. This approach uses the compiler to generate Check code, which is then loaded via dynamic ESM import to a generated ObjectURL (avoiding explicit calls to eval
). This approach works in Deno, it may also work in Browser environments that are behind content security restriction (but it would need testing)
import { TypeCompiler, TypeCheck } from 'npm:@sinclair/typebox/compiler'
import { TSchema, Type } from 'npm:@sinclair/typebox'
/**
* Experimental workaround for content security restricted environments. Should work
* in Deno and Browser environments. Other enviroments such as Node, Bun, Cloudflare
* may have issues importing from instanced ObjectURL's.
*/
async function DynamicCompile<T extends TSchema>(schema: T, references: TSchema[] = []): Promise<TypeCheck<T>> {
const code = `export default function() { ${TypeCompiler.Code(schema, references)} }`
const blob = new Blob([code])
const url = URL.createObjectURL(blob)
const mod = await import(url)
const check = mod.default()
URL.revokeObjectURL(url)
return new TypeCheck(schema, references, check, code)
}
// ------------------------------------------------------------------
// Usage
// ------------------------------------------------------------------
const T = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const C = await DynamicCompile(T) // Compile
const R = C.Check({ x: 1, y: 2, z: 3 }) // true
const E = [...C.Errors({ x: 1 })] // [...errors]
You can try the above, but overall the recommendation is simply to use Value.* operations when working in content security restricted environments.
Hope this helps S
It does, thank you.
Will go the Value route then. As you said in the browser it is rarely a performance issue, just fiddely to use the right constuct in the right environment.
The suggested workaround will probably get shut down by browsers sooner or later as is still in effect dynamic eval. And for true high perf situations the diagnostics errors are not that critical.
On Fri, 25 Oct 2024, 17:44 Haydn Paterson, @.***> wrote:
@peterlundberg https://github.com/peterlundberg Hi,
The AOT options for TypeBox are currently limited to Check functionality only. For errors, these are obtained via calls to Value.Errors() which uses dynamic property checking which are not compiled. The Errors() function is shared between TypeCompiler and Value.* operations. Content-Security
The recommendation for Content Security restricted environments is to simply use Value.* functions instead of the compiler. These functions are simpler to use, and do not require eval to work. They do however come at a performance cost, but are perfectly adequate for most browser level workloads. Experimental
If you do need high performance checking browser side while operating in a eval restricted context, you can try the following. This approach uses the compiler to generate Check code, which is then loaded via dynamic ESM import to a generated ObjectURL (avoiding explicit calls to eval). This approach works in Deno, it may also work in Browser environments that are behind content security restriction (but it would need testing)
import { TypeCompiler, TypeCheck } from @./typebox/compiler'import { TSchema, Type } from @./typebox' /* Experimental workaround for content security restricted environments. Should work in Deno and Browser environments. Other enviroments such as Node, Bun, Cloudflare may have issues imported from instance ObjectURL. */async function DynamicCompile
(schema: T, references: TSchema[] = []): Promise<TypeCheck > { const code = export default function() { ${TypeCompiler.Code(T)} }
const blob = new Blob([code]) const url = URL.createObjectURL(blob) const mod = await import(url) const check = mod.default() URL.revokeObjectURL(url) return new TypeCheck(schema, references, check, code)} // ------------------------------------------------------------------// Usage// ------------------------------------------------------------------ const T = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number(),}) const C = await DynamicCompile(T) // Compile const R = C.Check({ x: 1, y: 2, z: 3 }) // true const E = [...C.Errors({ x: 1 })] // [...errors]
You can try the above, but overall the recommendation is simply to use Value.* operations when working in content security restricted environments.
Hope this helps S
— Reply to this email directly, view it on GitHub https://github.com/sinclairzx81/typebox/issues/1045#issuecomment-2438164296, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACHTN7WISZNKIJRX5UM4MDZ5JRNTAVCNFSM6AAAAABQTI5AQKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZYGE3DIMRZGY . You are receiving this because you were mentioned.Message ID: @.***>
@peterlundberg Alright cool. Will close off this issue for now.
Cheers! S
In a recent project I could not use TypeBoxes validation in the front end web app due to CSP rules not allowing
unsafe-eval'
. To work around this I followed TypeCompile docs and added a build step in our workspace to generate the validator as code instead.Note that even with language: 'typescript' I had to tweek the output to get something existing typescript code could import easily. That is fine but perhaps this issue and other docs could help others getting this to work.
However, the biggest problem is that I did not get the diagnostic errors for when check fails. Seams that
.Compile
and.Code
are not functionally equivalent? Is there some way to get the.Errors
functionality from AOT code that I have missed?