Repository: pyscript/pyscript
File: pyscriptjs/src/main.ts (TypeScript)

Code before:
    }

    // lifecycle (2)
    loadConfig() {
        // find the <py-config> tag. If not found, we get null which means
        // "use the default config"
        // XXX: we should actively complain if there are multiple <py-config>
        // and show a big error. PRs welcome :)
        logger.info('searching for <py-config>');
        const elements = document.getElementsByTagName('py-config');
        let el: Element | null = null;
        if (elements.length > 0) el = elements[0];
        if (elements.length >= 2) {
            showWarning(
                'Multiple <py-config> tags detected. Only the first is ' +
                    'going to be parsed, all the others will be ignored',
            );
        }
        this.config = loadConfigFromElement(el);
        if (this.config.execution_thread === 'worker' && crossOriginIsolated === false) {
            throw new UserError(
                ErrorCode.BAD_CONFIG,
                `execution_thread is set to "worker" but the following CORS headers are missing: ${JSON.stringify({
                    'Cross-Origin-Embedder-Policy': 'require-corp',
                    'Cross-Origin-Opener-Policy': 'same-origin',
                })}`,
            );
        }
        logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
    }

    _get_base_url(): string {
        // Note that this requires that pyscript is loaded via a <script>
        // tag. If we want to allow loading via an ES6 module in the future,
        // we need to think about some other strategy
        const elem = document.currentScript as HTMLScriptElement;
        const slash = elem.src.lastIndexOf('/');
        return elem.src.slice(0, slash);
    }

    async _startInterpreter_main(interpreter_cfg: InterpreterConfig) {
        logger.info('Starting the interpreter in the main thread');
        // this is basically equivalent to worker_initialize()
        const remote_interpreter = new RemoteInterpreter(interpreter_cfg.src);
        const { port1, port2 } = new Synclink.FakeMessageChannel() as unknown as MessageChannel;
        port1.start();
        port2.start();
        Synclink.expose(remote_interpreter, port2);
        const wrapped_remote_interpreter = Synclink.wrap(port1);

        this.logStatus(`Downloading ${interpreter_cfg.name}...`);

Review comment (bug):
How do we know this? There might be a different reason that `crossOriginIsolated` is `false`, like the site being served over http (not https) or a browser bug. In general error messages should only make assertions of facts that we **know** are true. The reason that we are throwing the error is because crossOriginIsolated is false. So this is what we should report. We can then follow that with a suggestion that we think most likely because we are missing one or both of these two headers.

The following is more in the direction I'd think:
```
When execution_thread is "worker", the site must be cross origin isolated 
but crossOriginIsolated is false. To be cross origin isolated, it must be served with the following headers:

The problem may be that one or both of these are missing.
[in followup PR add link to discussion in DOCs]
```

Code after:
    }

    // lifecycle (2)
    loadConfig() {
        // find the <py-config> tag. If not found, we get null which means
        // "use the default config"
        // XXX: we should actively complain if there are multiple <py-config>
        // and show a big error. PRs welcome :)
        logger.info('searching for <py-config>');
        const elements = document.getElementsByTagName('py-config');
        let el: Element | null = null;
        if (elements.length > 0) el = elements[0];
        if (elements.length >= 2) {
            showWarning(
                'Multiple <py-config> tags detected. Only the first is ' +
                    'going to be parsed, all the others will be ignored',
            );
        }
        this.config = loadConfigFromElement(el);
        if (this.config.execution_thread === 'worker' && crossOriginIsolated === false) {
            throw new UserError(
                ErrorCode.BAD_CONFIG,
                `When execution_thread is "worker", the site must be cross origin isolated, but crossOriginIsolated is false.
                To be cross origin isolated, the server must use https and also serve with the following headers: ${JSON.stringify(
                    {
                        'Cross-Origin-Embedder-Policy': 'require-corp',
                        'Cross-Origin-Opener-Policy': 'same-origin',
                    },
                )}.

                The problem may be that one or both of these are missing.
                `,
            );
        }
        logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
    }

    _get_base_url(): string {
        // Note that this requires that pyscript is loaded via a <script>
        // tag. If we want to allow loading via an ES6 module in the future,
        // we need to think about some other strategy
        const elem = document.currentScript as HTMLScriptElement;
        const slash = elem.src.lastIndexOf('/');
        return elem.src.slice(0, slash);
    }

    async _startInterpreter_main(interpreter_cfg: InterpreterConfig) {
        logger.info('Starting the interpreter in the main thread');
        // this is basically equivalent to worker_initialize()
        const remote_interpreter = new RemoteInterpreter(interpreter_cfg.src);
        const { port1, port2 } = new Synclink.FakeMessageChannel() as unknown as MessageChannel;