spec.txt

- Add the ability to specify the certs yourself (so you can specify your identity with a real cert)
    - Then use real certificates on the server
- Fix multiple clients on the same machines
    - Maybe we need to exchange link information
- The login emails SHOULD redirect, if the close doesn't works
    (due to copying the link instead of clicking it)

- Other stuff
    - JSON buffer serialize, which generates an object, that allows for rehydration of buffers
        - Also... static classes (maybe even static resources), so structures can be sent
    - Consider forcing everything to use real certs everywhere, and make generating and updating
        certs very easy.
        - ALTHOUGH, maybe this should just be a downstream user of socket-function thing, and not
            a requirement of socket-function?
            - This will be something where machines ask a server for an identity, and then the owner
                allows it (giving them a subdomain), verifying their ip, etc.
                - MAYBE we just automatically allow it? This works if we never use the root domain for anything,
                    and only use subdomains?
                    - Although, of course, sometimes we WILL want to group by domain, so it is more than just
                        identity, so having a way for the owner to authenticate it might be nice as well...
                - Once they are given an identity, they are allowed to request their certs are updated,
                    and to get the new certs (it probably won't happen automatically, so that we can create
                    many nodes and let them die, without having to try to track which still need certs to be updated)
        - ALSO, remember, for domains, use the domain as the nodeId, not the public key, that way it
            is more consistent.


- https://letx.ca:2542/?classGuid=RequireController-e2f811f3-14b8-4759-b0d6-73f14516cf1d&functionName=requireHTML&args=[%22./test/test%22]

================== SocketFunction ==================

- Uses proxies, so that functions can be called before we know the shape of interface
- Headers
    - Support enabling "Access-Control-Allow-Credentials"/Request.credentials=include, with a hardcoded list of domains
    - Support Access-Control-Allow-Origin, with a hardcoded list of domains
    - Always set
        - response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
        - response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
        - response.setHeader("Cross-Origin-Resource-Policy", "same-site");
    - Remember to set headers for OPTIONS, but then NOOP

// NOTE: It is not possible to expose different services over different ports in the same process.
//  Just run different processes if you want different services.
SocketFunction.expose(ExampleController);
// Global hooks are useful for authentication
SocketFunction.addGlobalHook<ExampleContext>(null as SocketFunctionHook);
// Mount only after exposing controllers and setting up hooks
SocketFunction.mount({ port: 40981 });


// callerId is set before each function (and part of the hook context)
let callerId = ExampleController[socket].callerId;
let result = await ExampleController[socket].nodes[callerId].exampleFunction("hi");


// An object with context information is available in each call (so arguments don't have to be modified)
//  - register will have a second generic argument that is context, so this will be typed
ExampleController[socket].callContext



// Clientside may also wish to expose controllers, possibly the same, or different.
SocketFunction.expose(ExampleControllerClient);
// We might also want global clientside hooks (for authentication)
SocketFunction.addGlobalClientHook<ExampleContext>(null as SocketFunctionHook);
let serverId = await SocketFunctions.connect({ host: "example.com", port: 40981 });
// Cached, so it can be put in a helper function and called every time a call is made
let serverId = SocketFunctions.connectSync({ host: "example.com", port: 40981 });
ExampleController[socket].nodes[serverId].exampleFunction("hi server");
// If you have multiple servers each with many endpoints, you can make helper functions like this:
function exampleControllers() {
    let serverId = SocketFunctions.connectSync({ host: "example.com", port: 40981 });
    return {
        ExampleController: ExampleController[socket].nodes[serverId],
        ... etc, with all controllers mapped like this:
    }
}


export class ExampleController {
    // Uses both types AND shape configuration, to prevent functions from accidentally being exposed
    //  on the public internet...
    //  - Type checking is done to ensure no functions are exposed that aren't in your type
    [socket] = SocketFunction.register<ExampleController, ExampleContext>("ExampleController-2a4b1bd1-d00f-4812-be32-c4466f3c354a", {
        exampleFunction: {
            // Hooks wrap the call, allowing them to cancel it, change arguments, change the output, run it
            //  on another thread, check permission, etc, etc
            //  - Hooks are asynchronously, so they can even trigger other calls, etc
            //  - Context is passed to hooks
            hooks: [] as SocketFunctionHook[],
            // Client hooks run before a call, on the client. They have a different context,
            //  because they won't have information such as caller ip, but they can wrap calls
            //  in mostly the similar way
            clientHooks: [] as SocketFunctionClientHook[]
        },
    });

    async exampleFunction(arg1: string) {

    }
}

// ALSO, a shim can be created to avoid exposing your source code to API users (such as webpage).
//  - If you want, you can have your implementation import your config shape from the client file,
//      that way you only need to write it once.
import type * as Base from "./ExampleController";
export class ExampleController {
    [socket] = SocketFunction.register<Base.ExampleController, ExampleContext>("ExampleController-2a4b1bd1-d00f-4812-be32-c4466f3c354a", {
        exampleFunction: {
            clientHooks: [] as SocketFunctionClientHook[]
        },
    });
}
